]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-sfc): fix type resolution for shared type w/ different generic parameters
authorEvan You <yyx990803@gmail.com>
Fri, 22 Dec 2023 16:43:22 +0000 (00:43 +0800)
committerEvan You <yyx990803@gmail.com>
Fri, 22 Dec 2023 16:44:26 +0000 (00:44 +0800)
close #9871

packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts
packages/compiler-sfc/src/script/resolveType.ts

index b67423e0a89d9de8db9c27cba4dd57105e86be0a..a8074419c38b23984c29d12c898882236ee12bb6 100644 (file)
@@ -939,6 +939,34 @@ describe('resolveType', () => {
         manufacturer: ['Object']
       })
     })
+
+    // #9871
+    test('shared generics with different args', () => {
+      const files = {
+        '/foo.ts': `export interface Foo<T> { value: T }`
+      }
+      const { props } = resolve(
+        `import type { Foo } from './foo'
+        defineProps<Foo<string>>()`,
+        files,
+        undefined,
+        `/One.vue`
+      )
+      expect(props).toStrictEqual({
+        value: ['String']
+      })
+      const { props: props2 } = resolve(
+        `import type { Foo } from './foo'
+        defineProps<Foo<number>>()`,
+        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<string, string> = {},
   options?: Partial<SFCScriptCompileOptions>,
-  sourceFileName: string = '/Test.vue'
+  sourceFileName: string = '/Test.vue',
+  invalidateCache = true
 ) {
   const { descriptor } = parse(`<script setup lang="ts">\n${code}\n</script>`, {
     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
index c7c1234002a9ce19f2829b27e0a076d915bcfedc..4dd6febf42df6012bababa2892724c2b791212cf 100644 (file)
@@ -90,7 +90,7 @@ export class TypeScope {
     public types: Record<string, ScopeTypeNode> = Object.create(null),
     public declares: Record<string, ScopeTypeNode> = Object.create(null)
   ) {}
-
+  isGenericScope = false
   resolvedImportSources: Record<string, string> = Object.create(null)
   exportedTypes: Record<string, ScopeTypeNode> = Object.create(null)
   exportedDeclares: Record<string, ScopeTypeNode> = Object.create(null)
@@ -121,15 +121,17 @@ export function resolveTypeElements(
   scope?: TypeScope,
   typeParameters?: Record<string, Node>
 ): 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<string, Node> = Object.create(null)
+        let typeParams: Record<string, Node> | 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(