feat(types): add emits and slots type to `FunctionalComponent` (#8644)

This commit is contained in:
三咲智子 Kevin Deng 2023-12-08 22:24:58 +08:00 committed by GitHub
parent bfb856565d
commit 927ab17cfc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 69 additions and 17 deletions

View File

@ -8,14 +8,22 @@ import {
FunctionalComponent,
ComponentPublicInstance,
toRefs,
SetupContext
SetupContext,
EmitsOptions
} from 'vue'
import { describe, expectAssignable, expectType, IsAny } from './utils'
declare function extractComponentOptions<Props, RawBindings>(
obj: Component<Props, RawBindings>
declare function extractComponentOptions<
Props,
RawBindings,
Emits extends EmitsOptions | Record<string, any[]>,
Slots extends Record<string, any>
>(
obj: Component<Props, RawBindings, any, any, any, Emits, Slots>
): {
props: Props
emits: Emits
slots: Slots
rawBindings: RawBindings
setup: ShallowUnwrapRef<RawBindings>
}
@ -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 { props } = extractComponentOptions(MyComponent)
const MyComponent: FunctionalComponent<Props, Emits, Slots> = (
props,
{ emit, slots }
) => {
expectType<Props>(props)
expectType<{
(event: 'change', value: string): void
(event: 'inc', value: number): void
}>(emit)
expectType<Slots>(slots)
}
expectType<number>(props.foo)
const { props, emits, slots } = extractComponentOptions(MyComponent)
expectType<Props>(props)
expectType<Emits>(emits)
expectType<Slots>(slots)
})
})
@ -481,4 +505,18 @@ describe('SetupContext', () => {
expectAssignable<SetupContext<{ b: () => 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)
})
})

View File

@ -51,7 +51,8 @@ import {
EmitFn,
emit,
normalizeEmitsOptions,
EmitsToProps
EmitsToProps,
ShortEmitsToObject
} from './componentEmits'
import {
EMPTY_OBJ,
@ -160,16 +161,17 @@ export interface ComponentInternalOptions {
export interface FunctionalComponent<
P = {},
E extends EmitsOptions = {},
S extends Record<string, any> = any
E extends EmitsOptions | Record<string, any[]> = {},
S extends Record<string, any> = any,
EE extends EmitsOptions = ShortEmitsToObject<E>
> extends ComponentInternalOptions {
// use of any here is intentional so it can be a valid JSX Element constructor
(
props: P & EmitsToProps<E>,
ctx: Omit<SetupContext<E, IfAny<S, {}, SlotsType<S>>>, 'expose'>
props: P & EmitsToProps<EE>,
ctx: Omit<SetupContext<EE, IfAny<S, {}, SlotsType<S>>>, 'expose'>
): any
props?: ComponentPropsOptions<P>
emits?: E | (keyof E)[]
emits?: EE | (keyof EE)[]
slots?: IfAny<S, Slots, SlotsType<S>>
inheritAttrs?: boolean
displayName?: string
@ -192,10 +194,12 @@ export type ConcreteComponent<
RawBindings = any,
D = any,
C extends ComputedOptions = ComputedOptions,
M extends MethodOptions = MethodOptions
M extends MethodOptions = MethodOptions,
E extends EmitsOptions | Record<string, any[]> = {},
S extends Record<string, any> = any
> =
| ComponentOptions<Props, RawBindings, D, C, M>
| FunctionalComponent<Props, any>
| FunctionalComponent<Props, E, S>
/**
* A type used in public APIs where a component type is expected.
@ -206,9 +210,11 @@ export type Component<
RawBindings = any,
D = any,
C extends ComputedOptions = ComputedOptions,
M extends MethodOptions = MethodOptions
M extends MethodOptions = MethodOptions,
E extends EmitsOptions | Record<string, any[]> = {},
S extends Record<string, any> = any
> =
| ConcreteComponent<Props, RawBindings, D, C, M>
| ConcreteComponent<Props, RawBindings, D, C, M, E, S>
| ComponentPublicInstanceConstructor<Props>
export type { ComponentOptions }

View File

@ -55,6 +55,12 @@ export type EmitsToProps<T extends EmitsOptions> = T extends string[]
}
: {}
export type ShortEmitsToObject<E> = E extends Record<string, any[]>
? {
[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]
>