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', () => {
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
...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
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)
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(
}
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(
// 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
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(