Skip to content

Commit 927ab17

Browse files
authoredDec 8, 2023
feat(types): add emits and slots type to FunctionalComponent (#8644)
1 parent bfb8565 commit 927ab17

File tree

3 files changed

+69
-17
lines changed

3 files changed

+69
-17
lines changed
 

‎packages/dts-test/component.test-d.ts

+44-6
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,22 @@ import {
88
FunctionalComponent,
99
ComponentPublicInstance,
1010
toRefs,
11-
SetupContext
11+
SetupContext,
12+
EmitsOptions
1213
} from 'vue'
1314
import { describe, expectAssignable, expectType, IsAny } from './utils'
1415

15-
declare function extractComponentOptions<Props, RawBindings>(
16-
obj: Component<Props, RawBindings>
16+
declare function extractComponentOptions<
17+
Props,
18+
RawBindings,
19+
Emits extends EmitsOptions | Record<string, any[]>,
20+
Slots extends Record<string, any>
21+
>(
22+
obj: Component<Props, RawBindings, any, any, any, Emits, Slots>
1723
): {
1824
props: Props
25+
emits: Emits
26+
slots: Slots
1927
rawBindings: RawBindings
2028
setup: ShallowUnwrapRef<RawBindings>
2129
}
@@ -455,11 +463,27 @@ describe('functional', () => {
455463
})
456464

457465
describe('typed', () => {
458-
const MyComponent: FunctionalComponent<{ foo: number }> = (_, _2) => {}
466+
type Props = { foo: number }
467+
type Emits = { change: [value: string]; inc: [value: number] }
468+
type Slots = { default: (scope: { foo: string }) => any }
469+
470+
const MyComponent: FunctionalComponent<Props, Emits, Slots> = (
471+
props,
472+
{ emit, slots }
473+
) => {
474+
expectType<Props>(props)
475+
expectType<{
476+
(event: 'change', value: string): void
477+
(event: 'inc', value: number): void
478+
}>(emit)
479+
expectType<Slots>(slots)
480+
}
459481

460-
const { props } = extractComponentOptions(MyComponent)
482+
const { props, emits, slots } = extractComponentOptions(MyComponent)
461483

462-
expectType<number>(props.foo)
484+
expectType<Props>(props)
485+
expectType<Emits>(emits)
486+
expectType<Slots>(slots)
463487
})
464488
})
465489

@@ -481,4 +505,18 @@ describe('SetupContext', () => {
481505

482506
expectAssignable<SetupContext<{ b: () => true }>>(wider)
483507
})
508+
509+
describe('short emits', () => {
510+
const {
511+
emit
512+
}: SetupContext<{
513+
a: [val: string]
514+
b: [val: number]
515+
}> = {} as any
516+
517+
expectType<{
518+
(event: 'a', val: string): void
519+
(event: 'b', val: number): void
520+
}>(emit)
521+
})
484522
})

‎packages/runtime-core/src/component.ts

+16-10
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ import {
5151
EmitFn,
5252
emit,
5353
normalizeEmitsOptions,
54-
EmitsToProps
54+
EmitsToProps,
55+
ShortEmitsToObject
5556
} from './componentEmits'
5657
import {
5758
EMPTY_OBJ,
@@ -160,16 +161,17 @@ export interface ComponentInternalOptions {
160161

161162
export interface FunctionalComponent<
162163
P = {},
163-
E extends EmitsOptions = {},
164-
S extends Record<string, any> = any
164+
E extends EmitsOptions | Record<string, any[]> = {},
165+
S extends Record<string, any> = any,
166+
EE extends EmitsOptions = ShortEmitsToObject<E>
165167
> extends ComponentInternalOptions {
166168
// use of any here is intentional so it can be a valid JSX Element constructor
167169
(
168-
props: P & EmitsToProps<E>,
169-
ctx: Omit<SetupContext<E, IfAny<S, {}, SlotsType<S>>>, 'expose'>
170+
props: P & EmitsToProps<EE>,
171+
ctx: Omit<SetupContext<EE, IfAny<S, {}, SlotsType<S>>>, 'expose'>
170172
): any
171173
props?: ComponentPropsOptions<P>
172-
emits?: E | (keyof E)[]
174+
emits?: EE | (keyof EE)[]
173175
slots?: IfAny<S, Slots, SlotsType<S>>
174176
inheritAttrs?: boolean
175177
displayName?: string
@@ -192,10 +194,12 @@ export type ConcreteComponent<
192194
RawBindings = any,
193195
D = any,
194196
C extends ComputedOptions = ComputedOptions,
195-
M extends MethodOptions = MethodOptions
197+
M extends MethodOptions = MethodOptions,
198+
E extends EmitsOptions | Record<string, any[]> = {},
199+
S extends Record<string, any> = any
196200
> =
197201
| ComponentOptions<Props, RawBindings, D, C, M>
198-
| FunctionalComponent<Props, any>
202+
| FunctionalComponent<Props, E, S>
199203

200204
/**
201205
* A type used in public APIs where a component type is expected.
@@ -206,9 +210,11 @@ export type Component<
206210
RawBindings = any,
207211
D = any,
208212
C extends ComputedOptions = ComputedOptions,
209-
M extends MethodOptions = MethodOptions
213+
M extends MethodOptions = MethodOptions,
214+
E extends EmitsOptions | Record<string, any[]> = {},
215+
S extends Record<string, any> = any
210216
> =
211-
| ConcreteComponent<Props, RawBindings, D, C, M>
217+
| ConcreteComponent<Props, RawBindings, D, C, M, E, S>
212218
| ComponentPublicInstanceConstructor<Props>
213219

214220
export type { ComponentOptions }

‎packages/runtime-core/src/componentEmits.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ export type EmitsToProps<T extends EmitsOptions> = T extends string[]
5555
}
5656
: {}
5757

58+
export type ShortEmitsToObject<E> = E extends Record<string, any[]>
59+
? {
60+
[K in keyof E]: (...args: E[K]) => any
61+
}
62+
: E
63+
5864
export type EmitFn<
5965
Options = ObjectEmitsOptions,
6066
Event extends keyof Options = keyof Options
@@ -66,7 +72,9 @@ export type EmitFn<
6672
{
6773
[key in Event]: Options[key] extends (...args: infer Args) => any
6874
? (event: key, ...args: Args) => void
69-
: (event: key, ...args: any[]) => void
75+
: Options[key] extends any[]
76+
? (event: key, ...args: Options[key]) => void
77+
: (event: key, ...args: any[]) => void
7078
}[Event]
7179
>
7280

0 commit comments

Comments
 (0)
Please sign in to comment.