Skip to content

Commit

Permalink
fix(component-type-helpers): correct type inference for FunctionalCom…
Browse files Browse the repository at this point in the history
…ponent (#3766)

Co-authored-by: Johnson Chu <johnsoncodehk@gmail.com>
  • Loading branch information
pinguet62 and johnsoncodehk committed Dec 5, 2023
1 parent 6428aab commit 8caca96
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 4 deletions.
25 changes: 25 additions & 0 deletions packages/component-meta/tests/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,19 @@ const worker = (checker: ComponentMetaChecker, withTsconfig: boolean) => describ
expect(onBaz?.schema).toEqual([]);
});

test('reference-type-events for generic', () => {
const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/generic/component.vue');
const meta = checker.getComponentMeta(componentPath);

expect(meta.type).toEqual(TypeMeta.Function);

const onBar = meta.events.find(event => event.name === 'bar');

expect(onBar).toBeDefined();
expect(onBar?.type).toEqual('number');
expect(onBar?.signature).toEqual('(e: "bar", data: number): void');
});

test('template-slots', () => {
const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/template-slots/component.vue');
const meta = checker.getComponentMeta(componentPath);
Expand Down Expand Up @@ -563,6 +576,18 @@ const worker = (checker: ComponentMetaChecker, withTsconfig: boolean) => describ
expect(d).toBeDefined();
});

test('template-slots for generic', () => {
const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/generic/component.vue');
const meta = checker.getComponentMeta(componentPath);

expect(meta.type).toEqual(TypeMeta.Function);

expect(meta.slots.find(slot =>
slot.name === 'default'
&& slot.type === '{ foo: number; }'
)).toBeDefined();
});

test('template-slots without a script block', () => {
const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/template-slots/component-no-script.vue');
const meta = checker.getComponentMeta(componentPath);
Expand Down
4 changes: 2 additions & 2 deletions packages/component-type-helpers/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ export type ComponentProps<T> =

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

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

export type ComponentExposed<T> =
Expand Down
4 changes: 2 additions & 2 deletions packages/component-type-helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ export type ComponentProps<T> =
export type ComponentSlots<T> =
T extends new () => { $slots: infer S; } ? NonNullable<S> :
T extends (props: any, ctx: { slots: infer S; }, ...args: any) => any ? NonNullable<S> :
T extends (props: any, ctx: { slots: infer S; attrs: any; emit: any; }, ...args: any) => any ? NonNullable<S> :
{};
export type ComponentEmit<T> =
T extends new () => { $emit: infer E; } ? NonNullable<E> :
T extends (props: any, ctx: { emit: infer E; }, ...args: any) => any ? NonNullable<E> :
T extends (props: any, ctx: { slots: any; attrs: any; emit: infer E; }, ...args: any) => any ? NonNullable<E> :
{};
export type ComponentExposed<T> =
Expand Down
53 changes: 53 additions & 0 deletions packages/tsc/tests/__snapshots__/dts.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,59 @@ export default _default;
"
`;

exports[`vue-tsc-dts > Input: generic/component.vue, Output: generic/component.vue.d.ts 1`] = `
"declare const _default: <T>(__VLS_props: {
onBar?: (data: number) => any;
foo: number;
} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, __VLS_ctx?: {
attrs: any;
emit: (e: 'bar', data: number) => void;
slots: Readonly<{
default?(data: {
foo: number;
}): any;
}>;
}, __VLS_expose?: (exposed: import("vue").ShallowUnwrapRef<{
baz: number;
}>) => void, __VLS_setup?: Promise<{
props: {
onBar?: (data: number) => any;
foo: number;
} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps;
expose(exposed: import("vue").ShallowUnwrapRef<{
baz: number;
}>): void;
attrs: any;
slots: Readonly<{
default?(data: {
foo: number;
}): any;
}>;
emit: (e: 'bar', data: number) => void;
}>) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}> & {
__ctx?: {
props: {
onBar?: (data: number) => any;
foo: number;
} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps;
expose(exposed: import("vue").ShallowUnwrapRef<{
baz: number;
}>): void;
attrs: any;
slots: Readonly<{
default?(data: {
foo: number;
}): any;
}>;
emit: (e: 'bar', data: number) => void;
};
};
export default _default;
"
`;
exports[`vue-tsc-dts > Input: non-component/component.ts, Output: non-component/component.d.ts 1`] = `
"declare const _default: {};
export default _default;
Expand Down
6 changes: 6 additions & 0 deletions test-workspace/component-meta/generic/component.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<script setup lang="ts" generic="T">
defineProps<{ foo: number }>();
defineEmits<{ (e: 'bar', data: number): void }>();
defineExpose({ baz: {} as number });
defineSlots<{ default?(data: { foo: number }): any }>();
</script>

0 comments on commit 8caca96

Please sign in to comment.