Skip to content

Commit bb8cdca

Browse files
committed
fix(compiler-sfc): support proper type arguments for defineEmit helper
fix #2874
1 parent 2793bc0 commit bb8cdca

File tree

4 files changed

+40
-14
lines changed

4 files changed

+40
-14
lines changed

packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -692,15 +692,15 @@ return { a, b, c, d, x }
692692
}"
693693
`;
694694
695-
exports[`SFC compile <script setup> with TypeScript defineEmit w/ type (union) 1`] = `
695+
exports[`SFC compile <script setup> with TypeScript defineEmit w/ type (type literal w/ call signatures) 1`] = `
696696
"import { defineComponent as _defineComponent } from 'vue'
697697
698698
699699
export default _defineComponent({
700700
expose: [],
701701
emits: [\\"foo\\", \\"bar\\", \\"baz\\"] as unknown as undefined,
702702
setup(__props, { emit }: {
703-
emit: (((e: 'foo' | 'bar') => void) | ((e: 'baz', id: number) => void)),
703+
emit: ({(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}),
704704
slots: any,
705705
attrs: any
706706
}) {

packages/compiler-sfc/__tests__/compileScript.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,18 @@ const emit = defineEmit(['a', 'b'])
532532

533533
test('defineEmit w/ type (union)', () => {
534534
const type = `((e: 'foo' | 'bar') => void) | ((e: 'baz', id: number) => void)`
535+
expect(() =>
536+
compile(`
537+
<script setup lang="ts">
538+
import { defineEmit } from 'vue'
539+
const emit = defineEmit<${type}>()
540+
</script>
541+
`)
542+
).toThrow()
543+
})
544+
545+
test('defineEmit w/ type (type literal w/ call signatures)', () => {
546+
const type = `{(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}`
535547
const { content } = compile(`
536548
<script setup lang="ts">
537549
import { defineEmit } from 'vue'

packages/compiler-sfc/src/compileScript.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import {
2020
Statement,
2121
Expression,
2222
LabeledStatement,
23-
TSUnionType,
24-
CallExpression
23+
CallExpression,
24+
RestElement
2525
} from '@babel/types'
2626
import { walk } from 'estree-walker'
2727
import { RawSourceMap } from 'source-map'
@@ -184,7 +184,7 @@ export function compileScript(
184184
let propsTypeDecl: TSTypeLiteral | undefined
185185
let propsIdentifier: string | undefined
186186
let emitRuntimeDecl: Node | undefined
187-
let emitTypeDecl: TSFunctionType | TSUnionType | undefined
187+
let emitTypeDecl: TSFunctionType | TSTypeLiteral | undefined
188188
let emitIdentifier: string | undefined
189189
let hasAwait = false
190190
let hasInlinedSsrRenderFn = false
@@ -300,13 +300,13 @@ export function compileScript(
300300
const typeArg = node.typeParameters.params[0]
301301
if (
302302
typeArg.type === 'TSFunctionType' ||
303-
typeArg.type === 'TSUnionType'
303+
typeArg.type === 'TSTypeLiteral'
304304
) {
305305
emitTypeDecl = typeArg
306306
} else {
307307
error(
308308
`type argument passed to ${DEFINE_EMIT}() must be a function type ` +
309-
`or a union of function types.`,
309+
`or a literal type with call signatures.`,
310310
typeArg
311311
)
312312
}
@@ -1304,20 +1304,25 @@ function toRuntimeTypeString(types: string[]) {
13041304
}
13051305

13061306
function extractRuntimeEmits(
1307-
node: TSFunctionType | TSUnionType,
1307+
node: TSFunctionType | TSTypeLiteral,
13081308
emits: Set<string>
13091309
) {
1310-
if (node.type === 'TSUnionType') {
1311-
for (let t of node.types) {
1312-
if (t.type === 'TSParenthesizedType') t = t.typeAnnotation
1313-
if (t.type === 'TSFunctionType') {
1314-
extractRuntimeEmits(t, emits)
1310+
if (node.type === 'TSTypeLiteral') {
1311+
for (let t of node.members) {
1312+
if (t.type === 'TSCallSignatureDeclaration') {
1313+
extractEventNames(t.parameters[0], emits)
13151314
}
13161315
}
13171316
return
1317+
} else {
1318+
extractEventNames(node.parameters[0], emits)
13181319
}
1320+
}
13191321

1320-
const eventName = node.parameters[0]
1322+
function extractEventNames(
1323+
eventName: Identifier | RestElement,
1324+
emits: Set<string>
1325+
) {
13211326
if (
13221327
eventName.type === 'Identifier' &&
13231328
eventName.typeAnnotation &&

test-dts/setupHelpers.test-d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ describe('defineEmit w/ type declaration', () => {
5656
emit()
5757
// @ts-expect-error
5858
emit('bar')
59+
60+
type Emits = { (e: 'foo' | 'bar'): void; (e: 'baz', id: number): void }
61+
const emit2 = defineEmit<Emits>()
62+
63+
emit2('foo')
64+
emit2('bar')
65+
emit2('baz', 123)
66+
// @ts-expect-error
67+
emit2('baz')
5968
})
6069

6170
describe('defineEmit w/ runtime declaration', () => {

0 commit comments

Comments
 (0)