From d9dd628800ae32e673bdfabfe79f1988037991d0 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 21 Aug 2025 09:48:40 +0800 Subject: [PATCH] fix(compiler-sfc): improve type inference for generic type aliases types (#12876) close #12872 --- .../compileScript/resolveType.spec.ts | 17 ++++- .../compiler-sfc/src/script/resolveType.ts | 62 ++++++++++++++----- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts index c6a2f9c38d..c0f4db8208 100644 --- a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts @@ -538,7 +538,7 @@ describe('resolveType', () => { expect(props).toStrictEqual({ foo: ['Symbol', 'String', 'Number'], - bar: [UNKNOWN_TYPE], + bar: ['String', 'Number'], }) }) @@ -749,7 +749,7 @@ describe('resolveType', () => { }) }) - test('fallback to Unknown', () => { + test('with intersection type', () => { expect( resolve(` type Brand = T & {}; @@ -758,7 +758,18 @@ describe('resolveType', () => { }>() `).props, ).toStrictEqual({ - foo: [UNKNOWN_TYPE], + foo: ['String', 'Object'], + }) + }) + + test('with union type', () => { + expect( + resolve(` + type Wrapped = T | symbol | number + defineProps<{foo?: Wrapped}>() + `).props, + ).toStrictEqual({ + foo: ['Boolean', 'Symbol', 'Number'], }) }) }) diff --git a/packages/compiler-sfc/src/script/resolveType.ts b/packages/compiler-sfc/src/script/resolveType.ts index 910e8839a3..d8f4307005 100644 --- a/packages/compiler-sfc/src/script/resolveType.ts +++ b/packages/compiler-sfc/src/script/resolveType.ts @@ -1500,6 +1500,7 @@ export function inferRuntimeType( node: Node & MaybeWithScope, scope: TypeScope = node._ownerScope || ctxToScope(ctx), isKeyOf = false, + typeParameters?: Record, ): string[] { try { switch (node.type) { @@ -1588,19 +1589,43 @@ export function inferRuntimeType( case 'TSTypeReference': { const resolved = resolveTypeReference(ctx, node, scope) if (resolved) { - // #13240 - // Special case for function type aliases to ensure correct runtime behavior - // other type aliases still fallback to unknown as before - if ( - resolved.type === 'TSTypeAliasDeclaration' && - resolved.typeAnnotation.type === 'TSFunctionType' - ) { - return ['Function'] + if (resolved.type === 'TSTypeAliasDeclaration') { + // #13240 + // Special case for function type aliases to ensure correct runtime behavior + // other type aliases still fallback to unknown as before + if (resolved.typeAnnotation.type === 'TSFunctionType') { + return ['Function'] + } + + if (node.typeParameters) { + const typeParams: Record = Object.create(null) + if (resolved.typeParameters) { + resolved.typeParameters.params.forEach((p, i) => { + typeParams![p.name] = node.typeParameters!.params[i] + }) + } + return inferRuntimeType( + ctx, + resolved.typeAnnotation, + resolved._ownerScope, + isKeyOf, + typeParams, + ) + } } + return inferRuntimeType(ctx, resolved, resolved._ownerScope, isKeyOf) } - if (node.typeName.type === 'Identifier') { + if (typeParameters && typeParameters[node.typeName.name]) { + return inferRuntimeType( + ctx, + typeParameters[node.typeName.name], + scope, + isKeyOf, + typeParameters, + ) + } if (isKeyOf) { switch (node.typeName.name) { case 'String': @@ -1733,11 +1758,15 @@ export function inferRuntimeType( return inferRuntimeType(ctx, node.typeAnnotation, scope) case 'TSUnionType': - return flattenTypes(ctx, node.types, scope, isKeyOf) + return flattenTypes(ctx, node.types, scope, isKeyOf, typeParameters) case 'TSIntersectionType': { - return flattenTypes(ctx, node.types, scope, isKeyOf).filter( - t => t !== UNKNOWN_TYPE, - ) + return flattenTypes( + ctx, + node.types, + scope, + isKeyOf, + typeParameters, + ).filter(t => t !== UNKNOWN_TYPE) } case 'TSEnumDeclaration': @@ -1808,14 +1837,17 @@ function flattenTypes( types: TSType[], scope: TypeScope, isKeyOf: boolean = false, + typeParameters: Record | undefined = undefined, ): string[] { if (types.length === 1) { - return inferRuntimeType(ctx, types[0], scope, isKeyOf) + return inferRuntimeType(ctx, types[0], scope, isKeyOf, typeParameters) } return [ ...new Set( ([] as string[]).concat( - ...types.map(t => inferRuntimeType(ctx, t, scope, isKeyOf)), + ...types.map(t => + inferRuntimeType(ctx, t, scope, isKeyOf, typeParameters), + ), ), ), ] -- 2.47.3