From a8d0b1b38b092ec2d10b32bedcee2eea33b77657 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sat, 23 Dec 2023 00:43:22 +0800 Subject: [PATCH] fix(compiler-sfc): fix type resolution for shared type w/ different generic parameters close #9871 --- .../compileScript/resolveType.spec.ts | 37 +++++++++++++++++-- .../compiler-sfc/src/script/resolveType.ts | 24 +++++++----- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts index b67423e0a89..a8074419c38 100644 --- a/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts @@ -939,6 +939,34 @@ describe('resolveType', () => { manufacturer: ['Object'] }) }) + + // #9871 + test('shared generics with different args', () => { + const files = { + '/foo.ts': `export interface Foo { value: T }` + } + const { props } = resolve( + `import type { Foo } from './foo' + defineProps>()`, + files, + undefined, + `/One.vue` + ) + expect(props).toStrictEqual({ + value: ['String'] + }) + const { props: props2 } = resolve( + `import type { Foo } from './foo' + defineProps>()`, + files, + undefined, + `/Two.vue`, + false /* do not invalidate cache */ + ) + expect(props2).toStrictEqual({ + value: ['Number'] + }) + }) }) describe('errors', () => { @@ -1012,7 +1040,8 @@ function resolve( code: string, files: Record = {}, options?: Partial, - sourceFileName: string = '/Test.vue' + sourceFileName: string = '/Test.vue', + invalidateCache = true ) { const { descriptor } = parse(``, { filename: sourceFileName @@ -1030,8 +1059,10 @@ function resolve( ...options }) - for (const file in files) { - invalidateTypeCache(file) + if (invalidateCache) { + for (const file in files) { + invalidateTypeCache(file) + } } // ctx.userImports is collected when calling compileScript(), but we are diff --git a/packages/compiler-sfc/src/script/resolveType.ts b/packages/compiler-sfc/src/script/resolveType.ts index c7c1234002a..4dd6febf42d 100644 --- a/packages/compiler-sfc/src/script/resolveType.ts +++ b/packages/compiler-sfc/src/script/resolveType.ts @@ -90,7 +90,7 @@ export class TypeScope { public types: Record = Object.create(null), public declares: Record = Object.create(null) ) {} - + isGenericScope = false resolvedImportSources: Record = Object.create(null) exportedTypes: Record = Object.create(null) exportedDeclares: Record = Object.create(null) @@ -121,15 +121,17 @@ export function resolveTypeElements( scope?: TypeScope, typeParameters?: Record ): ResolvedElements { - if (node._resolvedElements) { + const canCache = !typeParameters + if (canCache && node._resolvedElements) { return node._resolvedElements } - return (node._resolvedElements = innerResolveTypeElements( + const resolved = innerResolveTypeElements( ctx, node, node._ownerScope || scope || ctxToScope(ctx), typeParameters - )) + ) + return canCache ? (node._resolvedElements = resolved) : resolved } function innerResolveTypeElements( @@ -190,17 +192,18 @@ function innerResolveTypeElements( } const resolved = resolveTypeReference(ctx, node, scope) if (resolved) { - const typeParams: Record = Object.create(null) + let typeParams: Record | undefined if ( (resolved.type === 'TSTypeAliasDeclaration' || resolved.type === 'TSInterfaceDeclaration') && resolved.typeParameters && node.typeParameters ) { + typeParams = Object.create(null) resolved.typeParameters.params.forEach((p, i) => { let param = typeParameters && typeParameters[p.name] if (!param) param = node.typeParameters!.params[i] - typeParams[p.name] = param + typeParams![p.name] = param }) } return resolveTypeElements( @@ -297,6 +300,7 @@ function typeElementsToMap( // capture generic parameters on node's scope if (typeParameters) { scope = createChildScope(scope) + scope.isGenericScope = true Object.assign(scope.types, typeParameters) } ;(e as MaybeWithScope)._ownerScope = scope @@ -669,16 +673,18 @@ function resolveTypeReference( name?: string, onlyExported = false ): ScopeTypeNode | undefined { - if (node._resolvedReference) { + const canCache = !scope?.isGenericScope + if (canCache && node._resolvedReference) { return node._resolvedReference } - return (node._resolvedReference = innerResolveTypeReference( + const resolved = innerResolveTypeReference( ctx, scope || ctxToScope(ctx), name || getReferenceName(node), node, onlyExported - )) + ) + return canCache ? (node._resolvedReference = resolved) : resolved } function innerResolveTypeReference(