feat(compiler-sfc): support limited built-in utility types in macros

This commit is contained in:
Evan You 2023-04-13 10:28:22 +08:00
parent fb8ecc803e
commit 1cfab4c695
2 changed files with 97 additions and 7 deletions

View File

@ -190,6 +190,31 @@ describe('resolveType', () => {
})
})
test('utility type: Pick', () => {
expect(
resolve(`
type T = { foo: number, bar: string, baz: boolean }
type K = 'foo' | 'bar'
type Target = Pick<T, K>
`).elements
).toStrictEqual({
foo: ['Number'],
bar: ['String']
})
})
test('utility type: Omit', () => {
expect(
resolve(`
type T = { foo: number, bar: string, baz: boolean }
type K = 'foo' | 'bar'
type Target = Omit<T, K>
`).elements
).toStrictEqual({
baz: ['Boolean']
})
})
describe('errors', () => {
test('error on computed keys', () => {
expect(() => resolve(`type Target = { [Foo]: string }`)).toThrow(

View File

@ -72,8 +72,18 @@ function innerResolveTypeElements(
if (resolved) {
return resolveTypeElements(ctx, resolved)
} else {
// TODO Pick / Omit
ctx.error(`Failed to resolved type reference`, node)
const typeName = getReferenceName(node)
if (
typeof typeName === 'string' &&
// @ts-ignore
SupportedBuiltinsSet.has(typeName)
) {
return resolveBuiltin(ctx, node, typeName as any)
}
ctx.error(
`Failed to resolved type reference, or unsupported built-in utlility type.`,
node
)
}
}
case 'TSUnionType':
@ -290,17 +300,60 @@ function resolveTemplateKeys(
return res
}
const SupportedBuiltinsSet = new Set([
'Partial',
'Required',
'Readonly',
'Pick',
'Omit'
] as const)
type GetSetType<T> = T extends Set<infer V> ? V : never
function resolveBuiltin(
ctx: ScriptCompileContext,
node: TSTypeReference | TSExpressionWithTypeArguments,
name: GetSetType<typeof SupportedBuiltinsSet>
): ResolvedElements {
const t = resolveTypeElements(ctx, node.typeParameters!.params[0])
switch (name) {
case 'Partial':
case 'Required':
case 'Readonly':
return t
case 'Pick': {
const picked = resolveStringType(ctx, node.typeParameters!.params[1])
const res: ResolvedElements = {}
if (t.__callSignatures) addCallSignature(res, t.__callSignatures)
for (const key of picked) {
res[key] = t[key]
}
return res
}
case 'Omit':
const omitted = resolveStringType(ctx, node.typeParameters!.params[1])
const res: ResolvedElements = {}
if (t.__callSignatures) addCallSignature(res, t.__callSignatures)
for (const key in t) {
if (!omitted.includes(key)) {
res[key] = t[key]
}
}
return res
}
}
function resolveTypeReference(
ctx: ScriptCompileContext,
node: TSTypeReference | TSExpressionWithTypeArguments,
scope = getRootScope(ctx)
): Node | undefined {
const ref = node.type === 'TSTypeReference' ? node.typeName : node.expression
if (ref.type === 'Identifier') {
if (scope.imports[ref.name]) {
const name = getReferenceName(node)
if (typeof name === 'string') {
if (scope.imports[name]) {
// TODO external import
} else if (scope.types[ref.name]) {
return scope.types[ref.name]
} else if (scope.types[name]) {
return scope.types[name]
}
} else {
// TODO qualified name, e.g. Foo.Bar
@ -308,6 +361,18 @@ function resolveTypeReference(
}
}
function getReferenceName(
node: TSTypeReference | TSExpressionWithTypeArguments
): string | string[] {
const ref = node.type === 'TSTypeReference' ? node.typeName : node.expression
if (ref.type === 'Identifier') {
return ref.name
} else {
// TODO qualified name, e.g. Foo.Bar
return []
}
}
function getRootScope(ctx: ScriptCompileContext): TypeScope {
if (ctx.scope) {
return ctx.scope