Skip to content

Commit b133e0f

Browse files
authored
fix(compiler-sfc): support defineEmits type reference with unions (#8299)
close #7943
1 parent 17d37d6 commit b133e0f

File tree

4 files changed

+66
-17
lines changed

4 files changed

+66
-17
lines changed

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

+18
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,24 @@ export default /*#__PURE__*/_defineComponent({
191191
192192
193193
194+
return { emit }
195+
}
196+
197+
})"
198+
`;
199+
200+
exports[`defineEmits > w/ type (type references in union) 1`] = `
201+
"import { defineComponent as _defineComponent } from 'vue'
202+
type BaseEmit = \\"change\\"
203+
type Emit = \\"some\\" | \\"emit\\" | BaseEmit
204+
205+
export default /*#__PURE__*/_defineComponent({
206+
emits: [\\"some\\", \\"emit\\", \\"change\\", \\"another\\"],
207+
setup(__props, { expose: __expose, emit }) {
208+
__expose();
209+
210+
211+
194212
return { emit }
195213
}
196214

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

+17
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,23 @@ const emit = defineEmits(['a', 'b'])
179179
assertCode(content)
180180
})
181181

182+
// #7943
183+
test('w/ type (type references in union)', () => {
184+
const { content } = compile(`
185+
<script setup lang="ts">
186+
type BaseEmit = "change"
187+
type Emit = "some" | "emit" | BaseEmit
188+
const emit = defineEmits<{
189+
(e: Emit): void;
190+
(e: "another", val: string): void;
191+
}>();
192+
</script>
193+
`)
194+
195+
expect(content).toMatch(`emits: ["some", "emit", "change", "another"]`)
196+
assertCode(content)
197+
})
198+
182199
describe('errors', () => {
183200
test('w/ both type and non-type args', () => {
184201
expect(() => {

packages/compiler-sfc/src/script/defineEmits.ts

+11-17
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Identifier, LVal, Node, RestElement } from '@babel/types'
22
import { isCallOf } from './utils'
33
import { ScriptCompileContext } from './context'
4-
import { resolveTypeElements } from './resolveType'
4+
import { resolveTypeElements, resolveUnionType } from './resolveType'
55

66
export const DEFINE_EMITS = 'defineEmits'
77

@@ -65,7 +65,7 @@ function extractRuntimeEmits(ctx: ScriptCompileContext): Set<string> {
6565
const node = ctx.emitsTypeDecl!
6666

6767
if (node.type === 'TSFunctionType') {
68-
extractEventNames(node.parameters[0], emits)
68+
extractEventNames(ctx, node.parameters[0], emits)
6969
return emits
7070
}
7171

@@ -85,14 +85,15 @@ function extractRuntimeEmits(ctx: ScriptCompileContext): Set<string> {
8585
)
8686
}
8787
for (const call of calls) {
88-
extractEventNames(call.parameters[0], emits)
88+
extractEventNames(ctx, call.parameters[0], emits)
8989
}
9090
}
9191

9292
return emits
9393
}
9494

9595
function extractEventNames(
96+
ctx: ScriptCompileContext,
9697
eventName: Identifier | RestElement,
9798
emits: Set<string>
9899
) {
@@ -101,22 +102,15 @@ function extractEventNames(
101102
eventName.typeAnnotation &&
102103
eventName.typeAnnotation.type === 'TSTypeAnnotation'
103104
) {
104-
const typeNode = eventName.typeAnnotation.typeAnnotation
105-
if (typeNode.type === 'TSLiteralType') {
106-
if (
107-
typeNode.literal.type !== 'UnaryExpression' &&
108-
typeNode.literal.type !== 'TemplateLiteral'
109-
) {
110-
emits.add(String(typeNode.literal.value))
111-
}
112-
} else if (typeNode.type === 'TSUnionType') {
113-
for (const t of typeNode.types) {
105+
const types = resolveUnionType(ctx, eventName.typeAnnotation.typeAnnotation)
106+
107+
for (const type of types) {
108+
if (type.type === 'TSLiteralType') {
114109
if (
115-
t.type === 'TSLiteralType' &&
116-
t.literal.type !== 'UnaryExpression' &&
117-
t.literal.type !== 'TemplateLiteral'
110+
type.literal.type !== 'UnaryExpression' &&
111+
type.literal.type !== 'TemplateLiteral'
118112
) {
119-
emits.add(String(t.literal.value))
113+
emits.add(String(type.literal.value))
120114
}
121115
}
122116
}

packages/compiler-sfc/src/script/resolveType.ts

+20
Original file line numberDiff line numberDiff line change
@@ -1637,3 +1637,23 @@ function resolveReturnType(
16371637
return resolved.returnType
16381638
}
16391639
}
1640+
1641+
export function resolveUnionType(
1642+
ctx: TypeResolveContext,
1643+
node: Node & MaybeWithScope & { _resolvedElements?: ResolvedElements },
1644+
scope?: TypeScope
1645+
): Node[] {
1646+
if (node.type === 'TSTypeReference') {
1647+
const resolved = resolveTypeReference(ctx, node, scope)
1648+
if (resolved) node = resolved
1649+
}
1650+
1651+
let types: Node[]
1652+
if (node.type === 'TSUnionType') {
1653+
types = node.types.flatMap(node => resolveUnionType(ctx, node, scope))
1654+
} else {
1655+
types = [node]
1656+
}
1657+
1658+
return types
1659+
}

0 commit comments

Comments
 (0)