From 4e561cca12ca82cd12b1f7bc450a6a13a36f6a9e Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 14 Feb 2025 11:23:07 +0800 Subject: [PATCH 1/3] feat(compiler-sfc): enhance type resolution with support for generic type parameters --- .../compileScript/resolveType.spec.ts | 24 ++++++++- .../compiler-sfc/src/script/resolveType.ts | 53 ++++++++++++++++--- 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts index 816d74bc649..cf96d5858fb 100644 --- a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts @@ -521,7 +521,7 @@ describe('resolveType', () => { expect(props).toStrictEqual({ foo: ['Symbol', 'String', 'Number'], - bar: [UNKNOWN_TYPE], + bar: ['String', 'Number'], }) }) @@ -779,6 +779,28 @@ describe('resolveType', () => { }) }) + test('generic with union type', () => { + expect( + resolve(` + type Wrapped = T | symbol | number + defineProps<{foo?: Wrapped}>() + `).props, + ).toStrictEqual({ + foo: ['Boolean', 'Symbol', 'Number'], + }) + }) + + test('generic type /w intersection', () => { + expect( + resolve(` + type Wrapped = T & symbol & number + defineProps<{foo?: Wrapped}>() + `).props, + ).toStrictEqual({ + foo: ['Boolean', 'Symbol', 'Number'], + }) + }) + test('generic from external-file', () => { const files = { '/foo.ts': 'export type P = { foo: T }', diff --git a/packages/compiler-sfc/src/script/resolveType.ts b/packages/compiler-sfc/src/script/resolveType.ts index 6bb647f11ff..38726896b99 100644 --- a/packages/compiler-sfc/src/script/resolveType.ts +++ b/packages/compiler-sfc/src/script/resolveType.ts @@ -1466,6 +1466,7 @@ export function inferRuntimeType( node: Node & MaybeWithScope, scope: TypeScope = node._ownerScope || ctxToScope(ctx), isKeyOf = false, + typeParameters: Record | undefined = undefined, ): string[] { try { switch (node.type) { @@ -1554,10 +1555,39 @@ export function inferRuntimeType( case 'TSTypeReference': { const resolved = resolveTypeReference(ctx, node, scope) if (resolved) { - return inferRuntimeType(ctx, resolved, resolved._ownerScope, isKeyOf) + if (!node.typeParameters) { + return inferRuntimeType( + ctx, + resolved, + resolved._ownerScope, + isKeyOf, + ) + } else if (resolved.type === 'TSTypeAliasDeclaration') { + const typeParams = 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, + ) + } } - 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': @@ -1690,11 +1720,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': @@ -1765,14 +1799,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), + ), ), ), ] From bd80a9c6dc049bafce04073f443e94c40ff2937d Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 14 Feb 2025 17:48:41 +0800 Subject: [PATCH 2/3] test: adjust test --- .../__tests__/compileScript/resolveType.spec.ts | 11 ----------- packages/compiler-sfc/src/script/resolveType.ts | 14 +++++--------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts index cf96d5858fb..7716763b8b1 100644 --- a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts @@ -790,17 +790,6 @@ describe('resolveType', () => { }) }) - test('generic type /w intersection', () => { - expect( - resolve(` - type Wrapped = T & symbol & number - defineProps<{foo?: Wrapped}>() - `).props, - ).toStrictEqual({ - foo: ['Boolean', 'Symbol', 'Number'], - }) - }) - test('generic from external-file', () => { const files = { '/foo.ts': 'export type P = { foo: T }', diff --git a/packages/compiler-sfc/src/script/resolveType.ts b/packages/compiler-sfc/src/script/resolveType.ts index 38726896b99..0e9920e99ac 100644 --- a/packages/compiler-sfc/src/script/resolveType.ts +++ b/packages/compiler-sfc/src/script/resolveType.ts @@ -1466,7 +1466,7 @@ export function inferRuntimeType( node: Node & MaybeWithScope, scope: TypeScope = node._ownerScope || ctxToScope(ctx), isKeyOf = false, - typeParameters: Record | undefined = undefined, + typeParameters?: Record, ): string[] { try { switch (node.type) { @@ -1563,7 +1563,7 @@ export function inferRuntimeType( isKeyOf, ) } else if (resolved.type === 'TSTypeAliasDeclaration') { - const typeParams = Object.create(null) + const typeParams: Record = Object.create(null) if (resolved.typeParameters) { resolved.typeParameters.params.forEach((p, i) => { typeParams![p.name] = node.typeParameters!.params[i] @@ -1722,13 +1722,9 @@ export function inferRuntimeType( case 'TSUnionType': return flattenTypes(ctx, node.types, scope, isKeyOf, typeParameters) case 'TSIntersectionType': { - return flattenTypes( - ctx, - node.types, - scope, - isKeyOf, - typeParameters, - ).filter(t => t !== UNKNOWN_TYPE) + return flattenTypes(ctx, node.types, scope, isKeyOf).filter( + t => t !== UNKNOWN_TYPE, + ) } case 'TSEnumDeclaration': From d98b4f8689c524572ada437ea6d44b3ae4536452 Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 14 Feb 2025 17:54:41 +0800 Subject: [PATCH 3/3] test: adjust test --- .../compiler-sfc/__tests__/compileScript/resolveType.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts index 7716763b8b1..1c0fe36d9dd 100644 --- a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts @@ -779,7 +779,7 @@ describe('resolveType', () => { }) }) - test('generic with union type', () => { + test('generic type parameter with union type', () => { expect( resolve(` type Wrapped = T | symbol | number