feat(compiler-sfc): support limited built-in utility types in macros
This commit is contained in:
parent
fb8ecc803e
commit
1cfab4c695
|
@ -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', () => {
|
describe('errors', () => {
|
||||||
test('error on computed keys', () => {
|
test('error on computed keys', () => {
|
||||||
expect(() => resolve(`type Target = { [Foo]: string }`)).toThrow(
|
expect(() => resolve(`type Target = { [Foo]: string }`)).toThrow(
|
||||||
|
|
|
@ -72,8 +72,18 @@ function innerResolveTypeElements(
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
return resolveTypeElements(ctx, resolved)
|
return resolveTypeElements(ctx, resolved)
|
||||||
} else {
|
} else {
|
||||||
// TODO Pick / Omit
|
const typeName = getReferenceName(node)
|
||||||
ctx.error(`Failed to resolved type reference`, 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':
|
case 'TSUnionType':
|
||||||
|
@ -290,17 +300,60 @@ function resolveTemplateKeys(
|
||||||
return res
|
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(
|
function resolveTypeReference(
|
||||||
ctx: ScriptCompileContext,
|
ctx: ScriptCompileContext,
|
||||||
node: TSTypeReference | TSExpressionWithTypeArguments,
|
node: TSTypeReference | TSExpressionWithTypeArguments,
|
||||||
scope = getRootScope(ctx)
|
scope = getRootScope(ctx)
|
||||||
): Node | undefined {
|
): Node | undefined {
|
||||||
const ref = node.type === 'TSTypeReference' ? node.typeName : node.expression
|
const name = getReferenceName(node)
|
||||||
if (ref.type === 'Identifier') {
|
if (typeof name === 'string') {
|
||||||
if (scope.imports[ref.name]) {
|
if (scope.imports[name]) {
|
||||||
// TODO external import
|
// TODO external import
|
||||||
} else if (scope.types[ref.name]) {
|
} else if (scope.types[name]) {
|
||||||
return scope.types[ref.name]
|
return scope.types[name]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO qualified name, e.g. Foo.Bar
|
// 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 {
|
function getRootScope(ctx: ScriptCompileContext): TypeScope {
|
||||||
if (ctx.scope) {
|
if (ctx.scope) {
|
||||||
return ctx.scope
|
return ctx.scope
|
||||||
|
|
Loading…
Reference in New Issue