Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: avoid losing generic types #3565

Merged
merged 17 commits into from
Sep 19, 2023
67 changes: 43 additions & 24 deletions packages/vue-language-core/src/generators/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -737,30 +737,50 @@ export function generate(
}
}

codes.push(
`const ${var_componentInstance} = ${var_functionalComponent}(`,
// diagnostic start
tagOffsets.length ? ['', 'template', tagOffsets[0], capabilitiesPresets.diagnosticOnly]
: dynamicTagExp ? ['', 'template', startTagOffset, capabilitiesPresets.diagnosticOnly]
: '',
'{ ',
);
if (!vueCompilerOptions.strictTemplates) {
// fix https://github.com/vuejs/language-tools/issues/3318
codes.push('...{ ');
if (vueCompilerOptions.strictTemplates) {
// with strictTemplates, generate once for props type-checking + instance type
codes.push(
`const ${var_componentInstance} = ${var_functionalComponent}(`,
// diagnostic start
tagOffsets.length ? ['', 'template', tagOffsets[0], capabilitiesPresets.diagnosticOnly]
: dynamicTagExp ? ['', 'template', startTagOffset, capabilitiesPresets.diagnosticOnly]
: '',
'{ ',
...createPropsCode(node, props, 'normal', propsFailedExps),
'}',
// diagnostic end
tagOffsets.length ? ['', 'template', tagOffsets[0] + tag.length, capabilitiesPresets.diagnosticOnly]
: dynamicTagExp ? ['', 'template', startTagOffset + tag.length, capabilitiesPresets.diagnosticOnly]
: '',
`, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}));\n`,
);
}
codes.push(...createPropsCode(node, props, 'normal', propsFailedExps));
if (!vueCompilerOptions.strictTemplates) {
codes.push('}, ');
else {
// without strictTemplates, this only for instacne type
codes.push(
`const ${var_componentInstance} = ${var_functionalComponent}(`,
'{ ',
...createPropsCode(node, props, 'extraReferences'),
'}',
`, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}));\n`,
);
// and this for props type-checking
codes.push(
`({} as (props: __VLS_FunctionalComponentProps<typeof ${var_originalComponent}, typeof ${var_componentInstance}> & Record<string, unknown>) => void)(`,
// diagnostic start
tagOffsets.length ? ['', 'template', tagOffsets[0], capabilitiesPresets.diagnosticOnly]
: dynamicTagExp ? ['', 'template', startTagOffset, capabilitiesPresets.diagnosticOnly]
: '',
'{ ',
...createPropsCode(node, props, 'normal', propsFailedExps),
'}',
// diagnostic end
tagOffsets.length ? ['', 'template', tagOffsets[0] + tag.length, capabilitiesPresets.diagnosticOnly]
: dynamicTagExp ? ['', 'template', startTagOffset + tag.length, capabilitiesPresets.diagnosticOnly]
: '',
`);\n`,
);
}
codes.push(
'}',
// diagnostic end
tagOffsets.length ? ['', 'template', tagOffsets[0] + tag.length, capabilitiesPresets.diagnosticOnly]
: dynamicTagExp ? ['', 'template', startTagOffset + tag.length, capabilitiesPresets.diagnosticOnly]
: '',
`, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}));\n`,
);

if (tag !== 'template' && tag !== 'slot') {
componentCtxVar = `__VLS_${elementIndex++}`;
Expand Down Expand Up @@ -980,7 +1000,7 @@ export function generate(
const eventVar = `__VLS_${elementIndex++}`;
codes.push(
`let ${eventVar} = { '${prop.arg.loc.source}': `,
`__VLS_pickEvent(${componentCtxVar}.emit!, '${prop.arg.loc.source}' as const, __VLS_componentProps(${componentVar}, ${componentInstanceVar})`,
`__VLS_pickEvent(${componentCtxVar}.emit!, '${prop.arg.loc.source}' as const, {} as __VLS_FunctionalComponentProps<typeof ${componentVar}, typeof ${componentInstanceVar}>`,
...createPropertyAccessCode([
camelize('on-' + prop.arg.loc.source), // onClickOutside
'template',
Expand Down Expand Up @@ -1167,7 +1187,6 @@ export function generate(
};
}


codes.push(`...{ `);
for (const prop of props) {
if (
Expand Down
14 changes: 7 additions & 7 deletions packages/vue-language-core/src/utils/globalTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ declare function __VLS_asFunctionalComponent<T, K = T extends new (...args: any)
}) => JSX.Element & { __ctx?: typeof ctx & { props?: typeof props; expose?(exposed: K): void; } }
: T extends () => any ? (props: {}, ctx?: any) => ReturnType<T>
: T extends (...args: any) => any ? T
: (_: T extends import('${vueCompilerOptions.lib}').VNode | import('${vueCompilerOptions.lib}').VNode[] | string ? {}: T${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: T${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'} } }; // IntrinsicElement
: (_: T extends import('${vueCompilerOptions.lib}').VNode | import('${vueCompilerOptions.lib}').VNode[] | string ? {}: T${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: (T extends import('${vueCompilerOptions.lib}').VNode ? {} : T extends object ? T : {})${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'} } }; // IntrinsicElement
declare function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): Parameters<T>['length'] extends 2 ? [any] : [];
declare function __VLS_pickEvent<Emit, K, E>(emit: Emit, emitKey: K, event: E): __VLS_FillingEventArg<
__VLS_PickNotAny<
Expand All @@ -106,16 +106,16 @@ declare function __VLS_pickEvent<Emit, K, E>(emit: Emit, emitKey: K, event: E):
>
> | undefined;
declare function __VLS_pickFunctionalComponentCtx<T, K>(comp: T, compInstance: K): __VLS_PickNotAny<
K extends { __ctx?: infer Ctx } ? Ctx : any,
T extends (props: any, ctx: infer Ctx) => any ? Ctx : any
'__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: infer Ctx } ? Ctx : never : any
, T extends (props: any, ctx: infer Ctx) => any ? Ctx : any
>;
type __VLS_FunctionalComponentProps<T, K> =
'__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: { props?: infer P } } ? NonNullable<P> : never
: T extends (props: infer P, ...args: any) => any ? P :
{};
type __VLS_AsFunctionOrAny<F> = unknown extends F ? any : ((...args: any) => any) extends F ? F : any;

declare function __VLS_normalizeSlot<S>(s: S): S extends () => infer R ? (props: {}) => R : S;
declare function __VLS_componentProps<T, K>(comp: T, fnReturn: K):
__VLS_PickNotAny<K, {}> extends { __ctx: { props: infer P } } ? NonNullable<P>
: T extends (props: infer P, ...args: any) => any ? NonNullable<P> :
{};
`.trim();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup lang="ts" generic="T, TValue extends undefined | string | number | symbol">
defineProps<{
items: Array<T>,
modelValue: TValue,
getKey: (item: T) => TValue,
}>();

defineSlots<{
default(props: { item: T }): any;
}>();
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script setup lang="ts">
import { exactType } from 'vue-tsc/shared';
import Comp from './Comp.vue';

const componentState = {
selected: '',
items: [null],
};
</script>

<template>
<Comp
v-slot="{ item }"
v-model="componentState.selected"
:items="componentState.items"
:get-key="(item) => {
exactType(item, null);
return '';
}"
>
{{ exactType(item, null) }}
</Comp>
</template>