diff --git a/packages/compiler-sfc/__tests__/compileScript/__snapshots__/defineEmits.spec.ts.snap b/packages/compiler-sfc/__tests__/compileScript/__snapshots__/defineEmits.spec.ts.snap index 729c019a555..a8bd930fbbc 100644 --- a/packages/compiler-sfc/__tests__/compileScript/__snapshots__/defineEmits.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/compileScript/__snapshots__/defineEmits.spec.ts.snap @@ -191,6 +191,24 @@ export default /*#__PURE__*/_defineComponent({ +return { emit } +} + +})" +`; + +exports[`defineEmits > w/ type (type references in union) 1`] = ` +"import { defineComponent as _defineComponent } from 'vue' +type BaseEmit = \\"change\\" + type Emit = \\"some\\" | \\"emit\\" | BaseEmit + +export default /*#__PURE__*/_defineComponent({ + emits: [\\"some\\", \\"emit\\", \\"change\\", \\"another\\"], + setup(__props, { expose: __expose, emit }) { + __expose(); + + + return { emit } } diff --git a/packages/compiler-sfc/__tests__/compileScript/defineEmits.spec.ts b/packages/compiler-sfc/__tests__/compileScript/defineEmits.spec.ts index 67d9674b54c..c5d2900a4e7 100644 --- a/packages/compiler-sfc/__tests__/compileScript/defineEmits.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript/defineEmits.spec.ts @@ -179,6 +179,23 @@ const emit = defineEmits(['a', 'b']) assertCode(content) }) + // #7943 + test('w/ type (type references in union)', () => { + const { content } = compile(` + + `) + + expect(content).toMatch(`emits: ["some", "emit", "change", "another"]`) + assertCode(content) + }) + describe('errors', () => { test('w/ both type and non-type args', () => { expect(() => { diff --git a/packages/compiler-sfc/src/script/defineEmits.ts b/packages/compiler-sfc/src/script/defineEmits.ts index e615cd71a0d..a50cf91fc4a 100644 --- a/packages/compiler-sfc/src/script/defineEmits.ts +++ b/packages/compiler-sfc/src/script/defineEmits.ts @@ -1,7 +1,7 @@ import { Identifier, LVal, Node, RestElement } from '@babel/types' import { isCallOf } from './utils' import { ScriptCompileContext } from './context' -import { resolveTypeElements } from './resolveType' +import { resolveTypeElements, resolveUnionType } from './resolveType' export const DEFINE_EMITS = 'defineEmits' @@ -65,7 +65,7 @@ function extractRuntimeEmits(ctx: ScriptCompileContext): Set { const node = ctx.emitsTypeDecl! if (node.type === 'TSFunctionType') { - extractEventNames(node.parameters[0], emits) + extractEventNames(ctx, node.parameters[0], emits) return emits } @@ -85,7 +85,7 @@ function extractRuntimeEmits(ctx: ScriptCompileContext): Set { ) } for (const call of calls) { - extractEventNames(call.parameters[0], emits) + extractEventNames(ctx, call.parameters[0], emits) } } @@ -93,6 +93,7 @@ function extractRuntimeEmits(ctx: ScriptCompileContext): Set { } function extractEventNames( + ctx: ScriptCompileContext, eventName: Identifier | RestElement, emits: Set ) { @@ -101,22 +102,15 @@ function extractEventNames( eventName.typeAnnotation && eventName.typeAnnotation.type === 'TSTypeAnnotation' ) { - const typeNode = eventName.typeAnnotation.typeAnnotation - if (typeNode.type === 'TSLiteralType') { - if ( - typeNode.literal.type !== 'UnaryExpression' && - typeNode.literal.type !== 'TemplateLiteral' - ) { - emits.add(String(typeNode.literal.value)) - } - } else if (typeNode.type === 'TSUnionType') { - for (const t of typeNode.types) { + const types = resolveUnionType(ctx, eventName.typeAnnotation.typeAnnotation) + + for (const type of types) { + if (type.type === 'TSLiteralType') { if ( - t.type === 'TSLiteralType' && - t.literal.type !== 'UnaryExpression' && - t.literal.type !== 'TemplateLiteral' + type.literal.type !== 'UnaryExpression' && + type.literal.type !== 'TemplateLiteral' ) { - emits.add(String(t.literal.value)) + emits.add(String(type.literal.value)) } } } diff --git a/packages/compiler-sfc/src/script/resolveType.ts b/packages/compiler-sfc/src/script/resolveType.ts index e79d21f8419..0f6514d2628 100644 --- a/packages/compiler-sfc/src/script/resolveType.ts +++ b/packages/compiler-sfc/src/script/resolveType.ts @@ -1605,3 +1605,23 @@ function resolveReturnType( return resolved.returnType } } + +export function resolveUnionType( + ctx: TypeResolveContext, + node: Node & MaybeWithScope & { _resolvedElements?: ResolvedElements }, + scope?: TypeScope +): Node[] { + if (node.type === 'TSTypeReference') { + const resolved = resolveTypeReference(ctx, node, scope) + if (resolved) node = resolved + } + + let types: Node[] + if (node.type === 'TSUnionType') { + types = node.types.flatMap(node => resolveUnionType(ctx, node, scope)) + } else { + types = [node] + } + + return types +}