diff --git a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts index 816d74bc649..1c0fe36d9dd 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,17 @@ describe('resolveType', () => { }) }) + test('generic type parameter with union type', () => { + 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..0e9920e99ac 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, ): 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: 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, + ) + } } - 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,7 +1720,7 @@ 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, @@ -1765,14 +1795,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), + ), ), ), ]