mirror of https://github.com/vuejs/core.git
fix(compiler-sfc): fix type resolution for shared type w/ different generic parameters
close #9871
This commit is contained in:
parent
1b522cae07
commit
a8d0b1b38b
|
@ -939,6 +939,34 @@ describe('resolveType', () => {
|
||||||
manufacturer: ['Object']
|
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', () => {
|
describe('errors', () => {
|
||||||
|
@ -1012,7 +1040,8 @@ function resolve(
|
||||||
code: string,
|
code: string,
|
||||||
files: Record<string, string> = {},
|
files: Record<string, string> = {},
|
||||||
options?: Partial<SFCScriptCompileOptions>,
|
options?: Partial<SFCScriptCompileOptions>,
|
||||||
sourceFileName: string = '/Test.vue'
|
sourceFileName: string = '/Test.vue',
|
||||||
|
invalidateCache = true
|
||||||
) {
|
) {
|
||||||
const { descriptor } = parse(`<script setup lang="ts">\n${code}\n</script>`, {
|
const { descriptor } = parse(`<script setup lang="ts">\n${code}\n</script>`, {
|
||||||
filename: sourceFileName
|
filename: sourceFileName
|
||||||
|
@ -1030,8 +1059,10 @@ function resolve(
|
||||||
...options
|
...options
|
||||||
})
|
})
|
||||||
|
|
||||||
for (const file in files) {
|
if (invalidateCache) {
|
||||||
invalidateTypeCache(file)
|
for (const file in files) {
|
||||||
|
invalidateTypeCache(file)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ctx.userImports is collected when calling compileScript(), but we are
|
// ctx.userImports is collected when calling compileScript(), but we are
|
||||||
|
|
|
@ -90,7 +90,7 @@ export class TypeScope {
|
||||||
public types: Record<string, ScopeTypeNode> = Object.create(null),
|
public types: Record<string, ScopeTypeNode> = Object.create(null),
|
||||||
public declares: Record<string, ScopeTypeNode> = Object.create(null)
|
public declares: Record<string, ScopeTypeNode> = Object.create(null)
|
||||||
) {}
|
) {}
|
||||||
|
isGenericScope = false
|
||||||
resolvedImportSources: Record<string, string> = Object.create(null)
|
resolvedImportSources: Record<string, string> = Object.create(null)
|
||||||
exportedTypes: Record<string, ScopeTypeNode> = Object.create(null)
|
exportedTypes: Record<string, ScopeTypeNode> = Object.create(null)
|
||||||
exportedDeclares: Record<string, ScopeTypeNode> = Object.create(null)
|
exportedDeclares: Record<string, ScopeTypeNode> = Object.create(null)
|
||||||
|
@ -121,15 +121,17 @@ export function resolveTypeElements(
|
||||||
scope?: TypeScope,
|
scope?: TypeScope,
|
||||||
typeParameters?: Record<string, Node>
|
typeParameters?: Record<string, Node>
|
||||||
): ResolvedElements {
|
): ResolvedElements {
|
||||||
if (node._resolvedElements) {
|
const canCache = !typeParameters
|
||||||
|
if (canCache && node._resolvedElements) {
|
||||||
return node._resolvedElements
|
return node._resolvedElements
|
||||||
}
|
}
|
||||||
return (node._resolvedElements = innerResolveTypeElements(
|
const resolved = innerResolveTypeElements(
|
||||||
ctx,
|
ctx,
|
||||||
node,
|
node,
|
||||||
node._ownerScope || scope || ctxToScope(ctx),
|
node._ownerScope || scope || ctxToScope(ctx),
|
||||||
typeParameters
|
typeParameters
|
||||||
))
|
)
|
||||||
|
return canCache ? (node._resolvedElements = resolved) : resolved
|
||||||
}
|
}
|
||||||
|
|
||||||
function innerResolveTypeElements(
|
function innerResolveTypeElements(
|
||||||
|
@ -190,17 +192,18 @@ function innerResolveTypeElements(
|
||||||
}
|
}
|
||||||
const resolved = resolveTypeReference(ctx, node, scope)
|
const resolved = resolveTypeReference(ctx, node, scope)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
const typeParams: Record<string, Node> = Object.create(null)
|
let typeParams: Record<string, Node> | undefined
|
||||||
if (
|
if (
|
||||||
(resolved.type === 'TSTypeAliasDeclaration' ||
|
(resolved.type === 'TSTypeAliasDeclaration' ||
|
||||||
resolved.type === 'TSInterfaceDeclaration') &&
|
resolved.type === 'TSInterfaceDeclaration') &&
|
||||||
resolved.typeParameters &&
|
resolved.typeParameters &&
|
||||||
node.typeParameters
|
node.typeParameters
|
||||||
) {
|
) {
|
||||||
|
typeParams = Object.create(null)
|
||||||
resolved.typeParameters.params.forEach((p, i) => {
|
resolved.typeParameters.params.forEach((p, i) => {
|
||||||
let param = typeParameters && typeParameters[p.name]
|
let param = typeParameters && typeParameters[p.name]
|
||||||
if (!param) param = node.typeParameters!.params[i]
|
if (!param) param = node.typeParameters!.params[i]
|
||||||
typeParams[p.name] = param
|
typeParams![p.name] = param
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return resolveTypeElements(
|
return resolveTypeElements(
|
||||||
|
@ -297,6 +300,7 @@ function typeElementsToMap(
|
||||||
// capture generic parameters on node's scope
|
// capture generic parameters on node's scope
|
||||||
if (typeParameters) {
|
if (typeParameters) {
|
||||||
scope = createChildScope(scope)
|
scope = createChildScope(scope)
|
||||||
|
scope.isGenericScope = true
|
||||||
Object.assign(scope.types, typeParameters)
|
Object.assign(scope.types, typeParameters)
|
||||||
}
|
}
|
||||||
;(e as MaybeWithScope)._ownerScope = scope
|
;(e as MaybeWithScope)._ownerScope = scope
|
||||||
|
@ -669,16 +673,18 @@ function resolveTypeReference(
|
||||||
name?: string,
|
name?: string,
|
||||||
onlyExported = false
|
onlyExported = false
|
||||||
): ScopeTypeNode | undefined {
|
): ScopeTypeNode | undefined {
|
||||||
if (node._resolvedReference) {
|
const canCache = !scope?.isGenericScope
|
||||||
|
if (canCache && node._resolvedReference) {
|
||||||
return node._resolvedReference
|
return node._resolvedReference
|
||||||
}
|
}
|
||||||
return (node._resolvedReference = innerResolveTypeReference(
|
const resolved = innerResolveTypeReference(
|
||||||
ctx,
|
ctx,
|
||||||
scope || ctxToScope(ctx),
|
scope || ctxToScope(ctx),
|
||||||
name || getReferenceName(node),
|
name || getReferenceName(node),
|
||||||
node,
|
node,
|
||||||
onlyExported
|
onlyExported
|
||||||
))
|
)
|
||||||
|
return canCache ? (node._resolvedReference = resolved) : resolved
|
||||||
}
|
}
|
||||||
|
|
||||||
function innerResolveTypeReference(
|
function innerResolveTypeReference(
|
||||||
|
|
Loading…
Reference in New Issue