Browse Source

types(runtime-core): provide valid type for default `$emit` (#1498)

Carlos Rodrigues 5 năm trước cách đây
mục cha
commit
1e90605c15

+ 1 - 1
packages/runtime-core/src/component.ts

@@ -79,7 +79,7 @@ export interface ComponentInternalOptions {
 
 
 export interface FunctionalComponent<
 export interface FunctionalComponent<
   P = {},
   P = {},
-  E extends EmitsOptions = Record<string, any>
+  E extends EmitsOptions = {}
 > extends ComponentInternalOptions {
 > extends ComponentInternalOptions {
   // use of any here is intentional so it can be a valid JSX Element constructor
   // use of any here is intentional so it can be a valid JSX Element constructor
   (props: P, ctx: SetupContext<E>): any
   (props: P, ctx: SetupContext<E>): any

+ 9 - 7
packages/runtime-core/src/componentEmits.ts

@@ -25,13 +25,15 @@ export type EmitFn<
   Event extends keyof Options = keyof Options
   Event extends keyof Options = keyof Options
 > = Options extends any[]
 > = Options extends any[]
   ? (event: Options[0], ...args: any[]) => void
   ? (event: Options[0], ...args: any[]) => void
-  : UnionToIntersection<
-      {
-        [key in Event]: Options[key] extends ((...args: infer Args) => any)
-          ? (event: key, ...args: Args) => void
-          : (event: key, ...args: any[]) => void
-      }[Event]
-    >
+  : {} extends Options // if the emit is empty object (usually the default value for emit) should be converted to function
+    ? (event: string, ...args: any[]) => void
+    : UnionToIntersection<
+        {
+          [key in Event]: Options[key] extends ((...args: infer Args) => any)
+            ? (event: key, ...args: Args) => void
+            : (event: key, ...args: any[]) => void
+        }[Event]
+      >
 
 
 export function emit(
 export function emit(
   instance: ComponentInternalInstance,
   instance: ComponentInternalInstance,

+ 3 - 2
packages/runtime-core/src/h.ts

@@ -12,6 +12,7 @@ import { isObject, isArray } from '@vue/shared'
 import { RawSlots } from './componentSlots'
 import { RawSlots } from './componentSlots'
 import { FunctionalComponent, Component } from './component'
 import { FunctionalComponent, Component } from './component'
 import { ComponentOptions } from './componentOptions'
 import { ComponentOptions } from './componentOptions'
+import { EmitsOptions } from './componentEmits'
 
 
 // `h` is a more user-friendly version of `createVNode` that allows omitting the
 // `h` is a more user-friendly version of `createVNode` that allows omitting the
 // props when possible. It is intended for manually written render functions.
 // props when possible. It is intended for manually written render functions.
@@ -107,8 +108,8 @@ export function h(
 ): VNode
 ): VNode
 
 
 // functional component
 // functional component
-export function h<P>(
-  type: FunctionalComponent<P>,
+export function h<P, E extends EmitsOptions = {}>(
+  type: FunctionalComponent<P, E>,
   props?: (RawProps & P) | ({} extends P ? null : never),
   props?: (RawProps & P) | ({} extends P ? null : never),
   children?: RawChildren | RawSlots
   children?: RawChildren | RawSlots
 ): VNode
 ): VNode

+ 15 - 1
test-dts/defineComponent.test-d.tsx

@@ -6,7 +6,8 @@ import {
   reactive,
   reactive,
   createApp,
   createApp,
   expectError,
   expectError,
-  expectType
+  expectType,
+  ComponentPublicInstance
 } from './index'
 } from './index'
 
 
 describe('with object props', () => {
 describe('with object props', () => {
@@ -669,4 +670,17 @@ describe('emits', () => {
       expectError(this.$emit('nope'))
       expectError(this.$emit('nope'))
     }
     }
   })
   })
+
+  // without emits
+  defineComponent({
+    setup(props, { emit }) {
+      emit('test', 1)
+      emit('test')
+    }
+  })
+
+  // emit should be valid when ComponentPublicInstance is used.
+  const instance = {} as ComponentPublicInstance
+  instance.$emit('test', 1)
+  instance.$emit('test')
 })
 })