Skip to content

Commit 8caca96

Browse files
fix(component-type-helpers): correct type inference for FunctionalComponent (#3766)
Co-authored-by: Johnson Chu <[email protected]>
1 parent 6428aab commit 8caca96

File tree

5 files changed

+88
-4
lines changed

5 files changed

+88
-4
lines changed

packages/component-meta/tests/index.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,19 @@ const worker = (checker: ComponentMetaChecker, withTsconfig: boolean) => describ
535535
expect(onBaz?.schema).toEqual([]);
536536
});
537537

538+
test('reference-type-events for generic', () => {
539+
const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/generic/component.vue');
540+
const meta = checker.getComponentMeta(componentPath);
541+
542+
expect(meta.type).toEqual(TypeMeta.Function);
543+
544+
const onBar = meta.events.find(event => event.name === 'bar');
545+
546+
expect(onBar).toBeDefined();
547+
expect(onBar?.type).toEqual('number');
548+
expect(onBar?.signature).toEqual('(e: "bar", data: number): void');
549+
});
550+
538551
test('template-slots', () => {
539552
const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/template-slots/component.vue');
540553
const meta = checker.getComponentMeta(componentPath);
@@ -563,6 +576,18 @@ const worker = (checker: ComponentMetaChecker, withTsconfig: boolean) => describ
563576
expect(d).toBeDefined();
564577
});
565578

579+
test('template-slots for generic', () => {
580+
const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/generic/component.vue');
581+
const meta = checker.getComponentMeta(componentPath);
582+
583+
expect(meta.type).toEqual(TypeMeta.Function);
584+
585+
expect(meta.slots.find(slot =>
586+
slot.name === 'default'
587+
&& slot.type === '{ foo: number; }'
588+
)).toBeDefined();
589+
});
590+
566591
test('template-slots without a script block', () => {
567592
const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/template-slots/component-no-script.vue');
568593
const meta = checker.getComponentMeta(componentPath);

packages/component-type-helpers/index.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ export type ComponentProps<T> =
1414

1515
export type ComponentSlots<T> =
1616
T extends new () => { $slots: infer S; } ? NonNullable<S> :
17-
T extends (props: any, ctx: { slots: infer S; }, ...args: any) => any ? NonNullable<S> :
17+
T extends (props: any, ctx: { slots: infer S; attrs: any; emit: any; }, ...args: any) => any ? NonNullable<S> :
1818
{};
1919

2020
export type ComponentEmit<T> =
2121
T extends new () => { $emit: infer E; } ? NonNullable<E> :
22-
T extends (props: any, ctx: { emit: infer E; }, ...args: any) => any ? NonNullable<E> :
22+
T extends (props: any, ctx: { slots: any; attrs: any; emit: infer E; }, ...args: any) => any ? NonNullable<E> :
2323
{};
2424

2525
export type ComponentExposed<T> =

packages/component-type-helpers/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ export type ComponentProps<T> =
1313
1414
export type ComponentSlots<T> =
1515
T extends new () => { $slots: infer S; } ? NonNullable<S> :
16-
T extends (props: any, ctx: { slots: infer S; }, ...args: any) => any ? NonNullable<S> :
16+
T extends (props: any, ctx: { slots: infer S; attrs: any; emit: any; }, ...args: any) => any ? NonNullable<S> :
1717
{};
1818
1919
export type ComponentEmit<T> =
2020
T extends new () => { $emit: infer E; } ? NonNullable<E> :
21-
T extends (props: any, ctx: { emit: infer E; }, ...args: any) => any ? NonNullable<E> :
21+
T extends (props: any, ctx: { slots: any; attrs: any; emit: infer E; }, ...args: any) => any ? NonNullable<E> :
2222
{};
2323
2424
export type ComponentExposed<T> =

packages/tsc/tests/__snapshots__/dts.spec.ts.snap

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,59 @@ export default _default;
2222
"
2323
`;
2424

25+
exports[`vue-tsc-dts > Input: generic/component.vue, Output: generic/component.vue.d.ts 1`] = `
26+
"declare const _default: <T>(__VLS_props: {
27+
onBar?: (data: number) => any;
28+
foo: number;
29+
} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, __VLS_ctx?: {
30+
attrs: any;
31+
emit: (e: 'bar', data: number) => void;
32+
slots: Readonly<{
33+
default?(data: {
34+
foo: number;
35+
}): any;
36+
}>;
37+
}, __VLS_expose?: (exposed: import("vue").ShallowUnwrapRef<{
38+
baz: number;
39+
}>) => void, __VLS_setup?: Promise<{
40+
props: {
41+
onBar?: (data: number) => any;
42+
foo: number;
43+
} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps;
44+
expose(exposed: import("vue").ShallowUnwrapRef<{
45+
baz: number;
46+
}>): void;
47+
attrs: any;
48+
slots: Readonly<{
49+
default?(data: {
50+
foo: number;
51+
}): any;
52+
}>;
53+
emit: (e: 'bar', data: number) => void;
54+
}>) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
55+
[key: string]: any;
56+
}> & {
57+
__ctx?: {
58+
props: {
59+
onBar?: (data: number) => any;
60+
foo: number;
61+
} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps;
62+
expose(exposed: import("vue").ShallowUnwrapRef<{
63+
baz: number;
64+
}>): void;
65+
attrs: any;
66+
slots: Readonly<{
67+
default?(data: {
68+
foo: number;
69+
}): any;
70+
}>;
71+
emit: (e: 'bar', data: number) => void;
72+
};
73+
};
74+
export default _default;
75+
"
76+
`;
77+
2578
exports[`vue-tsc-dts > Input: non-component/component.ts, Output: non-component/component.d.ts 1`] = `
2679
"declare const _default: {};
2780
export default _default;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script setup lang="ts" generic="T">
2+
defineProps<{ foo: number }>();
3+
defineEmits<{ (e: 'bar', data: number): void }>();
4+
defineExpose({ baz: {} as number });
5+
defineSlots<{ default?(data: { foo: number }): any }>();
6+
</script>

0 commit comments

Comments
 (0)