From 46b9ee14f2e8ae5bddb73ddc89d1b6656866e524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Mon, 26 Jun 2023 15:39:33 +0800 Subject: [PATCH] types: add emits and slots type to FunctionalComponent --- packages/dts-test/component.test-d.ts | 50 ++++++++++++++++++--- packages/runtime-core/src/component.ts | 24 ++++++---- packages/runtime-core/src/componentEmits.ts | 10 ++++- 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/packages/dts-test/component.test-d.ts b/packages/dts-test/component.test-d.ts index 5535419f198..98b3f42097b 100644 --- a/packages/dts-test/component.test-d.ts +++ b/packages/dts-test/component.test-d.ts @@ -8,14 +8,22 @@ import { FunctionalComponent, ComponentPublicInstance, toRefs, - SetupContext + SetupContext, + EmitsOptions } from 'vue' import { describe, expectAssignable, expectType, IsAny } from './utils' -declare function extractComponentOptions( - obj: Component +declare function extractComponentOptions< + Props, + RawBindings, + Emits extends EmitsOptions | Record, + Slots extends Record +>( + obj: Component ): { props: Props + emits: Emits + slots: Slots rawBindings: RawBindings setup: ShallowUnwrapRef } @@ -455,11 +463,27 @@ describe('functional', () => { }) describe('typed', () => { - const MyComponent: FunctionalComponent<{ foo: number }> = (_, _2) => {} + type Props = { foo: number } + type Emits = { change: [value: string]; inc: [value: number] } + type Slots = { default: (scope: { foo: string }) => any } + + const MyComponent: FunctionalComponent = ( + props, + { emit, slots } + ) => { + expectType(props) + expectType<{ + (event: 'change', value: string): void + (event: 'inc', value: number): void + }>(emit) + expectType(slots) + } - const { props } = extractComponentOptions(MyComponent) + const { props, emits, slots } = extractComponentOptions(MyComponent) - expectType(props.foo) + expectType(props) + expectType(emits) + expectType(slots) }) }) @@ -481,4 +505,18 @@ describe('SetupContext', () => { expectAssignable true }>>(wider) }) + + describe('short emits', () => { + const { + emit + }: SetupContext<{ + a: [val: string] + b: [val: number] + }> = {} as any + + expectType<{ + (event: 'a', val: string): void + (event: 'b', val: number): void + }>(emit) + }) }) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 309a7eb0e22..fb0b536d77e 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -50,7 +50,8 @@ import { ObjectEmitsOptions, EmitFn, emit, - normalizeEmitsOptions + normalizeEmitsOptions, + ShortEmitsToObject } from './componentEmits' import { EMPTY_OBJ, @@ -126,16 +127,19 @@ export interface ComponentInternalOptions { export interface FunctionalComponent< P = {}, - E extends EmitsOptions = {}, + E extends EmitsOptions | Record = {}, S extends Record = any > extends ComponentInternalOptions { // use of any here is intentional so it can be a valid JSX Element constructor ( props: P, - ctx: Omit>>, 'expose'> + ctx: Omit< + SetupContext, IfAny>>, + 'expose' + > ): any props?: ComponentPropsOptions

- emits?: E | (keyof E)[] + emits?: ShortEmitsToObject | (keyof ShortEmitsToObject)[] slots?: IfAny> inheritAttrs?: boolean displayName?: string @@ -158,10 +162,12 @@ export type ConcreteComponent< RawBindings = any, D = any, C extends ComputedOptions = ComputedOptions, - M extends MethodOptions = MethodOptions + M extends MethodOptions = MethodOptions, + E extends EmitsOptions | Record = {}, + S extends Record = any > = | ComponentOptions - | FunctionalComponent + | FunctionalComponent /** * A type used in public APIs where a component type is expected. @@ -172,9 +178,11 @@ export type Component< RawBindings = any, D = any, C extends ComputedOptions = ComputedOptions, - M extends MethodOptions = MethodOptions + M extends MethodOptions = MethodOptions, + E extends EmitsOptions | Record = {}, + S extends Record = any > = - | ConcreteComponent + | ConcreteComponent | ComponentPublicInstanceConstructor export type { ComponentOptions } diff --git a/packages/runtime-core/src/componentEmits.ts b/packages/runtime-core/src/componentEmits.ts index f3a30f7c953..893ebeaf3b7 100644 --- a/packages/runtime-core/src/componentEmits.ts +++ b/packages/runtime-core/src/componentEmits.ts @@ -55,6 +55,12 @@ export type EmitsToProps = T extends string[] } : {} +export type ShortEmitsToObject = E extends Record + ? { + [K in keyof E]: (...args: E[K]) => any + } + : E + export type EmitFn< Options = ObjectEmitsOptions, Event extends keyof Options = keyof Options @@ -66,7 +72,9 @@ export type EmitFn< { [key in Event]: Options[key] extends (...args: infer Args) => any ? (event: key, ...args: Args) => void - : (event: key, ...args: any[]) => void + : Options[key] extends any[] + ? (event: key, ...args: Options[key]) => void + : (event: key, ...args: any[]) => void }[Event] >