fix(compiler-sfc): support proper type arguments for defineEmit helper
fix #2874
This commit is contained in:
parent
2793bc0918
commit
bb8cdcad9f
@ -692,7 +692,7 @@ return { a, b, c, d, x }
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`SFC compile <script setup> with TypeScript defineEmit w/ type (union) 1`] = `
|
exports[`SFC compile <script setup> with TypeScript defineEmit w/ type (type literal w/ call signatures) 1`] = `
|
||||||
"import { defineComponent as _defineComponent } from 'vue'
|
"import { defineComponent as _defineComponent } from 'vue'
|
||||||
|
|
||||||
|
|
||||||
@ -700,7 +700,7 @@ export default _defineComponent({
|
|||||||
expose: [],
|
expose: [],
|
||||||
emits: [\\"foo\\", \\"bar\\", \\"baz\\"] as unknown as undefined,
|
emits: [\\"foo\\", \\"bar\\", \\"baz\\"] as unknown as undefined,
|
||||||
setup(__props, { emit }: {
|
setup(__props, { emit }: {
|
||||||
emit: (((e: 'foo' | 'bar') => void) | ((e: 'baz', id: number) => void)),
|
emit: ({(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}),
|
||||||
slots: any,
|
slots: any,
|
||||||
attrs: any
|
attrs: any
|
||||||
}) {
|
}) {
|
||||||
|
@ -532,6 +532,18 @@ const emit = defineEmit(['a', 'b'])
|
|||||||
|
|
||||||
test('defineEmit w/ type (union)', () => {
|
test('defineEmit w/ type (union)', () => {
|
||||||
const type = `((e: 'foo' | 'bar') => void) | ((e: 'baz', id: number) => void)`
|
const type = `((e: 'foo' | 'bar') => void) | ((e: 'baz', id: number) => void)`
|
||||||
|
expect(() =>
|
||||||
|
compile(`
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineEmit } from 'vue'
|
||||||
|
const emit = defineEmit<${type}>()
|
||||||
|
</script>
|
||||||
|
`)
|
||||||
|
).toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('defineEmit w/ type (type literal w/ call signatures)', () => {
|
||||||
|
const type = `{(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}`
|
||||||
const { content } = compile(`
|
const { content } = compile(`
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineEmit } from 'vue'
|
import { defineEmit } from 'vue'
|
||||||
|
@ -20,8 +20,8 @@ import {
|
|||||||
Statement,
|
Statement,
|
||||||
Expression,
|
Expression,
|
||||||
LabeledStatement,
|
LabeledStatement,
|
||||||
TSUnionType,
|
CallExpression,
|
||||||
CallExpression
|
RestElement
|
||||||
} from '@babel/types'
|
} from '@babel/types'
|
||||||
import { walk } from 'estree-walker'
|
import { walk } from 'estree-walker'
|
||||||
import { RawSourceMap } from 'source-map'
|
import { RawSourceMap } from 'source-map'
|
||||||
@ -184,7 +184,7 @@ export function compileScript(
|
|||||||
let propsTypeDecl: TSTypeLiteral | undefined
|
let propsTypeDecl: TSTypeLiteral | undefined
|
||||||
let propsIdentifier: string | undefined
|
let propsIdentifier: string | undefined
|
||||||
let emitRuntimeDecl: Node | undefined
|
let emitRuntimeDecl: Node | undefined
|
||||||
let emitTypeDecl: TSFunctionType | TSUnionType | undefined
|
let emitTypeDecl: TSFunctionType | TSTypeLiteral | undefined
|
||||||
let emitIdentifier: string | undefined
|
let emitIdentifier: string | undefined
|
||||||
let hasAwait = false
|
let hasAwait = false
|
||||||
let hasInlinedSsrRenderFn = false
|
let hasInlinedSsrRenderFn = false
|
||||||
@ -300,13 +300,13 @@ export function compileScript(
|
|||||||
const typeArg = node.typeParameters.params[0]
|
const typeArg = node.typeParameters.params[0]
|
||||||
if (
|
if (
|
||||||
typeArg.type === 'TSFunctionType' ||
|
typeArg.type === 'TSFunctionType' ||
|
||||||
typeArg.type === 'TSUnionType'
|
typeArg.type === 'TSTypeLiteral'
|
||||||
) {
|
) {
|
||||||
emitTypeDecl = typeArg
|
emitTypeDecl = typeArg
|
||||||
} else {
|
} else {
|
||||||
error(
|
error(
|
||||||
`type argument passed to ${DEFINE_EMIT}() must be a function type ` +
|
`type argument passed to ${DEFINE_EMIT}() must be a function type ` +
|
||||||
`or a union of function types.`,
|
`or a literal type with call signatures.`,
|
||||||
typeArg
|
typeArg
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1304,20 +1304,25 @@ function toRuntimeTypeString(types: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function extractRuntimeEmits(
|
function extractRuntimeEmits(
|
||||||
node: TSFunctionType | TSUnionType,
|
node: TSFunctionType | TSTypeLiteral,
|
||||||
emits: Set<string>
|
emits: Set<string>
|
||||||
) {
|
) {
|
||||||
if (node.type === 'TSUnionType') {
|
if (node.type === 'TSTypeLiteral') {
|
||||||
for (let t of node.types) {
|
for (let t of node.members) {
|
||||||
if (t.type === 'TSParenthesizedType') t = t.typeAnnotation
|
if (t.type === 'TSCallSignatureDeclaration') {
|
||||||
if (t.type === 'TSFunctionType') {
|
extractEventNames(t.parameters[0], emits)
|
||||||
extractRuntimeEmits(t, emits)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
extractEventNames(node.parameters[0], emits)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const eventName = node.parameters[0]
|
function extractEventNames(
|
||||||
|
eventName: Identifier | RestElement,
|
||||||
|
emits: Set<string>
|
||||||
|
) {
|
||||||
if (
|
if (
|
||||||
eventName.type === 'Identifier' &&
|
eventName.type === 'Identifier' &&
|
||||||
eventName.typeAnnotation &&
|
eventName.typeAnnotation &&
|
||||||
|
@ -56,6 +56,15 @@ describe('defineEmit w/ type declaration', () => {
|
|||||||
emit()
|
emit()
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
emit('bar')
|
emit('bar')
|
||||||
|
|
||||||
|
type Emits = { (e: 'foo' | 'bar'): void; (e: 'baz', id: number): void }
|
||||||
|
const emit2 = defineEmit<Emits>()
|
||||||
|
|
||||||
|
emit2('foo')
|
||||||
|
emit2('bar')
|
||||||
|
emit2('baz', 123)
|
||||||
|
// @ts-expect-error
|
||||||
|
emit2('baz')
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('defineEmit w/ runtime declaration', () => {
|
describe('defineEmit w/ runtime declaration', () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user