Skip to content

Commit

Permalink
fix: use isomorphic __VLS_ConstructorOverloads when emits type is n…
Browse files Browse the repository at this point in the history
…ot inlined (#3585)

Co-authored-by: Johnson Chu <[email protected]>
  • Loading branch information
so1ve and johnsoncodehk authored Sep 23, 2023
1 parent 34ac2d7 commit c466f51
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 54 deletions.
76 changes: 59 additions & 17 deletions packages/vue-language-core/src/generators/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -50,7 +49,6 @@ export function generate(
scriptSetupRanges = {
bindings: [],
emitsAssignName: undefined,
emitsTypeNums: 0,
exposeRuntimeArg: undefined,
leadingCommentEndOffset: 0,
importSectionEndOffset: 0,
Expand All @@ -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`);
Expand Down Expand Up @@ -108,7 +107,6 @@ export function generate(
};

function generateHelperTypes() {
let usedPrettify = false;
if (usedHelperTypes.DefinePropsToOptions) {
if (compilerOptions.exactOptionalPropertyTypes) {
codes.push(`type __VLS_TypePropsToRuntimeProps<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? { type: import('${vueCompilerOptions.lib}').PropType<T[K]> } : { type: import('${vueCompilerOptions.lib}').PropType<T[K]>, required: true } };\n`);
Expand All @@ -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<U> = __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> = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never;
type __VLS_IsAny<T> = 0 extends 1 & T ? true : false;
type __VLS_IsUnknown<T> = __VLS_IsAny<T> extends true ? false : unknown extends T ? true : false;
type __VLS_PopIntersectionFuncs<I> = I extends (...args: infer A) => infer R ? (...args: A) => R : never;
type __VLS_GetIntersectionFuncsLastOneFirstArg<I> = I extends (firstArg: infer F, ...rest: infer P) => void ? F : never;
type __VLS_GetIntersectionFuncsLastOneRestArg<I> = I extends (firstArg: infer F, ...rest: infer P) => void ? P : never;
type __VLS_NarrowIntersection<I, T> = I extends (T & infer R) ? __VLS_IsUnknown<R> extends true ? never : R : never;
type __VLS_Prepend<U, T extends any[]> = ((a: U, ...r: T) => void) extends (...r: infer R) => void ? R : never;
type __VLS_ExtractFirstArgRecursively<I, Result extends any[]> = {
1: Result;
0: __VLS_ExtractFirstArgRecursively<
__VLS_NarrowIntersection<I, __VLS_PopIntersectionFuncs<I>>,
__VLS_Prepend<__VLS_GetIntersectionFuncsLastOneFirstArg<I>, Result>
>;
}[[I] extends [never] ? 1 : 0];
type __VLS_ExtractRestArgRecursively<I, Result extends any[]> = {
1: Result;
0: __VLS_ExtractRestArgRecursively<
__VLS_NarrowIntersection<I, __VLS_PopIntersectionFuncs<I>>,
__VLS_Prepend<__VLS_GetIntersectionFuncsLastOneRestArg<I>, Result>
>;
}[[I] extends [never] ? 1 : 0];
type __VLS_RemoveLabels<Tuple, Result extends any[]> = Tuple extends [infer E, ...infer Rest] ? __VLS_RemoveLabels<Rest, [...Result, E]> : Result;
type __VLS_GetAllOverloadsFirstArg<I> = __VLS_RemoveLabels<__VLS_ExtractFirstArgRecursively<I, []>, []>;
type __VLS_GetAllOverloadsRestArg<I> = __VLS_RemoveLabels<__VLS_ExtractRestArgRecursively<I, []>, []>;
type __VLS_IndexOf<T extends unknown[], U extends unknown, Count extends 1[] = []> =
T extends [infer First, ...infer Rest] ? (
(<V>() => V extends First ? 1 : 0) extends
(<V>() => V extends U ? 1 : 0)
? Count['length']
: __VLS_IndexOf<Rest, U, [...Count, 1]>
) : -1;
type __VLS_Zip<Keys extends any[], Values extends any[]> = {
[K in Keys[number]]: Values[__VLS_IndexOf<Keys, K>]
};
type __VLS_OverloadUnion<T, U = unknown> = U & T extends (...args: infer A) => infer R
? U extends T
? never
: __VLS_OverloadUnion<T, Pick<T, keyof T> & U & ((...args: A) => R)> | ((...args: A) => R)
: never;
type __VLS_OverloadToIntersection<T> = __VLS_UnionToIntersection<Exclude<
__VLS_OverloadUnion<
(() => never) & T
>,
T extends () => never ? never : () => never
>>;
type __VLS_ConstructorOverloads<T, I = __VLS_OverloadToIntersection<T>, Z = __VLS_Zip<__VLS_GetAllOverloadsFirstArg<I>, __VLS_GetAllOverloadsRestArg<I>>> = {
[K in keyof Z]: (...args: Z[K]) => void;
};
`);
codes.push(`type __VLS_NormalizeEmits<T> = __VLS_ConstructorOverloads<T> & {
[K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never
}\n`);;
}\n`);
}
if (usedHelperTypes.WithTemplateSlots) {
codes.push(
Expand All @@ -157,7 +198,7 @@ export function generate(
if (usedHelperTypes.PropsChildren) {
codes.push(`type __VLS_PropsChildren<S> = { [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<T> = { [K in keyof T]: T[K]; } & {};\n`);
}
}
Expand Down Expand Up @@ -592,10 +633,11 @@ declare function defineProp<T>(value?: T | (() => T), required?: boolean, rest?:
}
if (scriptSetupRanges.defineEmits) {
usedHelperTypes.EmitsTypeHelpers = true;
usedHelperTypes.Prettify = true;
codes.push(
`emits: ({} as __VLS_UnionToIntersection<__VLS_NormalizeEmits<typeof `,
`emits: ({} as __VLS_Prettify<__VLS_UnionToIntersection<__VLS_NormalizeEmits<typeof `,
scriptSetupRanges.emitsAssignName ?? '__VLS_emit',
`>>),\n`,
`>>>),\n`,
);
}
}
Expand Down
2 changes: 0 additions & 2 deletions packages/vue-language-core/src/parsers/scriptSetupRanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -72,7 +71,6 @@ export function parseScriptSetupRanges(
propsTypeArg,
slotsAssignName,
emitsAssignName,
emitsTypeNums,
exposeRuntimeArg,
defineProp,
};
Expand Down
32 changes: 0 additions & 32 deletions packages/vue-language-core/src/utils/globalTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,35 +119,3 @@ type __VLS_AsFunctionOrAny<F> = unknown extends F ? any : ((...args: any) => any
declare function __VLS_normalizeSlot<S>(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}<T> =\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`;
}
}
16 changes: 16 additions & 0 deletions packages/vue-test-workspace/vue-tsc-dts/#3542/main.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">
type Emits = {
(e: 'a1', value: number): void;
(e: 'a2', value: number): void;
(e: 'a3', value: number): void;
(e: 'a4', value: number): void;
(e: 'a5', value: number): void;
(e: 'a6', value: number): void;
(e: 'a7', value: number): void;
(e: 'a8', value: number): void;
(e: 'a9', value: number): void;
(e: 'a10', value: number): void;
};
defineEmits<Emits>();
</script>
34 changes: 31 additions & 3 deletions packages/vue-tsc/tests/__snapshots__/dts.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -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<import(\\"vue\\").ExtractPropTypes<{}>> & {
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<import(\\"vue\\").ExtractPropTypes<{
foo: StringConstructor;
}>> & {
Expand Down Expand Up @@ -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<import(\\"vue\\").ExtractPropTypes<{}>> & {
onFoo?: (args_0: string) => any;
}, {}, {}>;
Expand Down

0 comments on commit c466f51

Please sign in to comment.