From c466f51b9265d5fdd26a6165e934b49046eb9203 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 24 Sep 2023 06:03:29 +0800 Subject: [PATCH] fix: use isomorphic `__VLS_ConstructorOverloads` when emits type is not inlined (#3585) Co-authored-by: Johnson Chu --- .../src/generators/script.ts | 76 ++++++++++++++----- .../src/parsers/scriptSetupRanges.ts | 2 - .../src/utils/globalTypes.ts | 32 -------- .../vue-tsc-dts/#3542/main.vue | 16 ++++ .../tests/__snapshots__/dts.spec.ts.snap | 34 ++++++++- 5 files changed, 106 insertions(+), 54 deletions(-) create mode 100644 packages/vue-test-workspace/vue-tsc-dts/#3542/main.vue diff --git a/packages/vue-language-core/src/generators/script.ts b/packages/vue-language-core/src/generators/script.ts index fe84e05077..5b9047dd72 100644 --- a/packages/vue-language-core/src/generators/script.ts +++ b/packages/vue-language-core/src/generators/script.ts @@ -9,7 +9,6 @@ import type { ScriptRanges } from '../parsers/scriptRanges'; import type { ScriptSetupRanges } from '../parsers/scriptSetupRanges'; import type { TextRange, VueCompilerOptions } from '../types'; import { Sfc } from '../types'; -import * as sharedTypes from '../utils/globalTypes'; import { getSlotsPropertyName, hyphenateTag } from '../utils/shared'; import { walkInterpolationFragment } from '../utils/transform'; @@ -50,7 +49,6 @@ export function generate( scriptSetupRanges = { bindings: [], emitsAssignName: undefined, - emitsTypeNums: 0, exposeRuntimeArg: undefined, leadingCommentEndOffset: 0, importSectionEndOffset: 0, @@ -74,6 +72,7 @@ export function generate( EmitsTypeHelpers: false, WithTemplateSlots: false, PropsChildren: false, + Prettify: false, }; codes.push(`/* ${Object.entries(vueCompilerOptions).map(([key, value]) => `${key}: ${JSON.stringify(value)}`).join(', ')} */\n`); @@ -108,7 +107,6 @@ export function generate( }; function generateHelperTypes() { - let usedPrettify = false; if (usedHelperTypes.DefinePropsToOptions) { if (compilerOptions.exactOptionalPropertyTypes) { codes.push(`type __VLS_TypePropsToRuntimeProps = { [K in keyof T]-?: {} extends Pick ? { type: import('${vueCompilerOptions.lib}').PropType } : { type: import('${vueCompilerOptions.lib}').PropType, required: true } };\n`); @@ -125,21 +123,64 @@ export function generate( default: D[K] }> : P[K] };\n`); - usedPrettify = true; + usedHelperTypes.Prettify = true; } if (usedHelperTypes.EmitsTypeHelpers) { - // fix https://github.com/vuejs/language-tools/issues/926 - codes.push('type __VLS_UnionToIntersection = __VLS_Prettify<(U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never>;\n'); - usedPrettify = true; - if (scriptSetupRanges && scriptSetupRanges.emitsTypeNums !== -1) { - codes.push(sharedTypes.genConstructorOverloads('__VLS_ConstructorOverloads', scriptSetupRanges.emitsTypeNums)); - } - else { - codes.push(sharedTypes.genConstructorOverloads('__VLS_ConstructorOverloads')); - } + codes.push(` + // fix https://github.com/vuejs/language-tools/issues/926 + type __VLS_UnionToIntersection = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never; + type __VLS_IsAny = 0 extends 1 & T ? true : false; + type __VLS_IsUnknown = __VLS_IsAny extends true ? false : unknown extends T ? true : false; + type __VLS_PopIntersectionFuncs = I extends (...args: infer A) => infer R ? (...args: A) => R : never; + type __VLS_GetIntersectionFuncsLastOneFirstArg = I extends (firstArg: infer F, ...rest: infer P) => void ? F : never; + type __VLS_GetIntersectionFuncsLastOneRestArg = I extends (firstArg: infer F, ...rest: infer P) => void ? P : never; + type __VLS_NarrowIntersection = I extends (T & infer R) ? __VLS_IsUnknown extends true ? never : R : never; + type __VLS_Prepend = ((a: U, ...r: T) => void) extends (...r: infer R) => void ? R : never; + type __VLS_ExtractFirstArgRecursively = { + 1: Result; + 0: __VLS_ExtractFirstArgRecursively< + __VLS_NarrowIntersection>, + __VLS_Prepend<__VLS_GetIntersectionFuncsLastOneFirstArg, Result> + >; + }[[I] extends [never] ? 1 : 0]; + type __VLS_ExtractRestArgRecursively = { + 1: Result; + 0: __VLS_ExtractRestArgRecursively< + __VLS_NarrowIntersection>, + __VLS_Prepend<__VLS_GetIntersectionFuncsLastOneRestArg, Result> + >; + }[[I] extends [never] ? 1 : 0]; + type __VLS_RemoveLabels = Tuple extends [infer E, ...infer Rest] ? __VLS_RemoveLabels : Result; + type __VLS_GetAllOverloadsFirstArg = __VLS_RemoveLabels<__VLS_ExtractFirstArgRecursively, []>; + type __VLS_GetAllOverloadsRestArg = __VLS_RemoveLabels<__VLS_ExtractRestArgRecursively, []>; + type __VLS_IndexOf = + T extends [infer First, ...infer Rest] ? ( + (() => V extends First ? 1 : 0) extends + (() => V extends U ? 1 : 0) + ? Count['length'] + : __VLS_IndexOf + ) : -1; + type __VLS_Zip = { + [K in Keys[number]]: Values[__VLS_IndexOf] + }; + type __VLS_OverloadUnion = U & T extends (...args: infer A) => infer R + ? U extends T + ? never + : __VLS_OverloadUnion & U & ((...args: A) => R)> | ((...args: A) => R) + : never; + type __VLS_OverloadToIntersection = __VLS_UnionToIntersection never) & T + >, + T extends () => never ? never : () => never + >>; + type __VLS_ConstructorOverloads, Z = __VLS_Zip<__VLS_GetAllOverloadsFirstArg, __VLS_GetAllOverloadsRestArg>> = { + [K in keyof Z]: (...args: Z[K]) => void; + }; +`); codes.push(`type __VLS_NormalizeEmits = __VLS_ConstructorOverloads & { [K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never - }\n`);; + }\n`); } if (usedHelperTypes.WithTemplateSlots) { codes.push( @@ -157,7 +198,7 @@ export function generate( if (usedHelperTypes.PropsChildren) { codes.push(`type __VLS_PropsChildren = { [K in keyof (boolean extends (JSX.ElementChildrenAttribute extends never ? true : false) ? never : JSX.ElementChildrenAttribute)]?: S; };\n`); } - if (usedPrettify) { + if (usedHelperTypes.Prettify) { codes.push(`type __VLS_Prettify = { [K in keyof T]: T[K]; } & {};\n`); } } @@ -592,10 +633,11 @@ declare function defineProp(value?: T | (() => T), required?: boolean, rest?: } if (scriptSetupRanges.defineEmits) { usedHelperTypes.EmitsTypeHelpers = true; + usedHelperTypes.Prettify = true; codes.push( - `emits: ({} as __VLS_UnionToIntersection<__VLS_NormalizeEmits>),\n`, + `>>>),\n`, ); } } diff --git a/packages/vue-language-core/src/parsers/scriptSetupRanges.ts b/packages/vue-language-core/src/parsers/scriptSetupRanges.ts index 0325b52be8..500b1a2d6f 100644 --- a/packages/vue-language-core/src/parsers/scriptSetupRanges.ts +++ b/packages/vue-language-core/src/parsers/scriptSetupRanges.ts @@ -21,7 +21,6 @@ export function parseScriptSetupRanges( let slotsAssignName: string | undefined; let emitsAssignName: string | undefined; let exposeRuntimeArg: TextRange | undefined; - let emitsTypeNums = -1; const definePropProposalA = vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition' || ast.getFullText().trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition'); const definePropProposalB = vueCompilerOptions.experimentalDefinePropProposal === 'johnsonEdition' || ast.getFullText().trimStart().startsWith('// @experimentalDefinePropProposal=johnsonEdition'); @@ -72,7 +71,6 @@ export function parseScriptSetupRanges( propsTypeArg, slotsAssignName, emitsAssignName, - emitsTypeNums, exposeRuntimeArg, defineProp, }; diff --git a/packages/vue-language-core/src/utils/globalTypes.ts b/packages/vue-language-core/src/utils/globalTypes.ts index 2ff2ef0734..1f68cbc072 100644 --- a/packages/vue-language-core/src/utils/globalTypes.ts +++ b/packages/vue-language-core/src/utils/globalTypes.ts @@ -119,35 +119,3 @@ type __VLS_AsFunctionOrAny = unknown extends F ? any : ((...args: any) => any declare function __VLS_normalizeSlot(s: S): S extends () => infer R ? (props: {}) => R : S; `.trim(); } - -// TODO: not working for overloads > n (n = 8) -// see: https://github.com/vuejs/language-tools/issues/60 -export function genConstructorOverloads(name = 'ConstructorOverloads', nums?: number) { - let code = ''; - code += `type ${name} =\n`; - if (nums === undefined) { - for (let i = 8; i >= 1; i--) { - gen(i); - } - } - else if (nums > 0) { - gen(nums); - } - code += `// 0\n`; - code += `{};\n`; - return code; - - function gen(i: number) { - code += `// ${i}\n`; - code += `T extends {\n`; - for (let j = 1; j <= i; j++) { - code += `(event: infer E${j}, ...payload: infer P${j}): void;\n`; - } - code += `} ? (\n`; - for (let j = 1; j <= i; j++) { - if (j > 1) code += '& '; - code += `(E${j} extends string ? { [K${j} in E${j}]: (...payload: P${j}) => void } : {})\n`; - } - code += `) :\n`; - } -} diff --git a/packages/vue-test-workspace/vue-tsc-dts/#3542/main.vue b/packages/vue-test-workspace/vue-tsc-dts/#3542/main.vue new file mode 100644 index 0000000000..4c2796e9d1 --- /dev/null +++ b/packages/vue-test-workspace/vue-tsc-dts/#3542/main.vue @@ -0,0 +1,16 @@ + diff --git a/packages/vue-tsc/tests/__snapshots__/dts.spec.ts.snap b/packages/vue-tsc/tests/__snapshots__/dts.spec.ts.snap index b36a5fbde5..72e11c8466 100644 --- a/packages/vue-tsc/tests/__snapshots__/dts.spec.ts.snap +++ b/packages/vue-tsc/tests/__snapshots__/dts.spec.ts.snap @@ -1,11 +1,39 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`vue-tsc-dts > Input: #3542/main.vue, Output: #3542/main.vue.d.ts 1`] = ` +"declare const _default: import(\\"vue\\").DefineComponent<{}, {}, {}, {}, {}, import(\\"vue\\").ComponentOptionsMixin, import(\\"vue\\").ComponentOptionsMixin, { + a1: (value: number) => void; + a2: (value: number) => void; + a3: (value: number) => void; + a4: (value: number) => void; + a5: (value: number) => void; + a6: (value: number) => void; + a7: (value: number) => void; + a8: (value: number) => void; + a9: (value: number) => void; + a10: (value: number) => void; +}, string, import(\\"vue\\").VNodeProps & import(\\"vue\\").AllowedComponentProps & import(\\"vue\\").ComponentCustomProps, Readonly> & { + onA1?: (value: number) => any; + onA2?: (value: number) => any; + onA3?: (value: number) => any; + onA4?: (value: number) => any; + onA5?: (value: number) => any; + onA6?: (value: number) => any; + onA7?: (value: number) => any; + onA8?: (value: number) => any; + onA9?: (value: number) => any; + onA10?: (value: number) => any; +}, {}, {}>; +export default _default; +" +`; + exports[`vue-tsc-dts > Input: components/script-setup.vue, Output: components/script-setup.vue.d.ts 1`] = ` "declare const _default: import(\\"vue\\").DefineComponent<{ foo: StringConstructor; }, {}, unknown, {}, {}, import(\\"vue\\").ComponentOptionsMixin, import(\\"vue\\").ComponentOptionsMixin, { - change: (...payload: any[]) => void; - delete: (...payload: any[]) => void; + change: (...args: any[]) => void; + delete: (...args: any[]) => void; }, string, import(\\"vue\\").VNodeProps & import(\\"vue\\").AllowedComponentProps & import(\\"vue\\").ComponentCustomProps, Readonly> & { @@ -136,7 +164,7 @@ export default _default; exports[`vue-tsc-dts > Input: defineEmits-new-syntax/main.vue, Output: defineEmits-new-syntax/main.vue.d.ts 1`] = ` "declare const _default: import(\\"vue\\").DefineComponent<{}, {}, {}, {}, {}, import(\\"vue\\").ComponentOptionsMixin, import(\\"vue\\").ComponentOptionsMixin, { - foo: (payload_0: string) => void; + foo: (args_0: string) => void; }, string, import(\\"vue\\").VNodeProps & import(\\"vue\\").AllowedComponentProps & import(\\"vue\\").ComponentCustomProps, Readonly> & { onFoo?: (args_0: string) => any; }, {}, {}>;