]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-sfc): support proper type arguments for defineEmit helper
authorEvan You <yyx990803@gmail.com>
Mon, 29 Mar 2021 20:38:07 +0000 (16:38 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 29 Mar 2021 20:38:07 +0000 (16:38 -0400)
fix #2874

packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
packages/compiler-sfc/__tests__/compileScript.spec.ts
packages/compiler-sfc/src/compileScript.ts
test-dts/setupHelpers.test-d.ts

index c27f0aeabe2565f2e34fb69e34d50ea243d6fb3e..f648f39e8faecbf2ebda529b5293ae10cebdd8cd 100644 (file)
@@ -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'
 
       
@@ -700,7 +700,7 @@ export default _defineComponent({
   expose: [],
   emits: [\\"foo\\", \\"bar\\", \\"baz\\"] as unknown as undefined,
   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,
         attrs: any
       }) {
index f083a0b6731e357c0cdb03575d7967f85628a9f8..41e3abe8c572f57897fdd7d142312e2e1b9f260f 100644 (file)
@@ -532,6 +532,18 @@ const emit = defineEmit(['a', 'b'])
 
     test('defineEmit w/ type (union)', () => {
       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(`
       <script setup lang="ts">
       import { defineEmit } from 'vue'
index 6d7d84ccadbf36d6bce79133dca80335c3ed68de..5bb8a2461a90c8dc69ab4035e027ab938fb53c85 100644 (file)
@@ -20,8 +20,8 @@ import {
   Statement,
   Expression,
   LabeledStatement,
-  TSUnionType,
-  CallExpression
+  CallExpression,
+  RestElement
 } from '@babel/types'
 import { walk } from 'estree-walker'
 import { RawSourceMap } from 'source-map'
@@ -184,7 +184,7 @@ export function compileScript(
   let propsTypeDecl: TSTypeLiteral | undefined
   let propsIdentifier: string | undefined
   let emitRuntimeDecl: Node | undefined
-  let emitTypeDecl: TSFunctionType | TSUnionType | undefined
+  let emitTypeDecl: TSFunctionType | TSTypeLiteral | undefined
   let emitIdentifier: string | undefined
   let hasAwait = false
   let hasInlinedSsrRenderFn = false
@@ -300,13 +300,13 @@ export function compileScript(
         const typeArg = node.typeParameters.params[0]
         if (
           typeArg.type === 'TSFunctionType' ||
-          typeArg.type === 'TSUnionType'
+          typeArg.type === 'TSTypeLiteral'
         ) {
           emitTypeDecl = typeArg
         } else {
           error(
             `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
           )
         }
@@ -1304,20 +1304,25 @@ function toRuntimeTypeString(types: string[]) {
 }
 
 function extractRuntimeEmits(
-  node: TSFunctionType | TSUnionType,
+  node: TSFunctionType | TSTypeLiteral,
   emits: Set<string>
 ) {
-  if (node.type === 'TSUnionType') {
-    for (let t of node.types) {
-      if (t.type === 'TSParenthesizedType') t = t.typeAnnotation
-      if (t.type === 'TSFunctionType') {
-        extractRuntimeEmits(t, emits)
+  if (node.type === 'TSTypeLiteral') {
+    for (let t of node.members) {
+      if (t.type === 'TSCallSignatureDeclaration') {
+        extractEventNames(t.parameters[0], emits)
       }
     }
     return
+  } else {
+    extractEventNames(node.parameters[0], emits)
   }
+}
 
-  const eventName = node.parameters[0]
+function extractEventNames(
+  eventName: Identifier | RestElement,
+  emits: Set<string>
+) {
   if (
     eventName.type === 'Identifier' &&
     eventName.typeAnnotation &&
index 09ced2d62c5926e2d7f2b98a3abc8126a2efb5a7..53d073fa616da09e96670591d15cc1075b19f89c 100644 (file)
@@ -56,6 +56,15 @@ describe('defineEmit w/ type declaration', () => {
   emit()
   // @ts-expect-error
   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', () => {