Skip to content

Commit 1cfab4c

Browse files
committed
feat(compiler-sfc): support limited built-in utility types in macros
1 parent fb8ecc8 commit 1cfab4c

File tree

2 files changed

+97
-7
lines changed

2 files changed

+97
-7
lines changed

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

+25
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,31 @@ describe('resolveType', () => {
190190
})
191191
})
192192

193+
test('utility type: Pick', () => {
194+
expect(
195+
resolve(`
196+
type T = { foo: number, bar: string, baz: boolean }
197+
type K = 'foo' | 'bar'
198+
type Target = Pick<T, K>
199+
`).elements
200+
).toStrictEqual({
201+
foo: ['Number'],
202+
bar: ['String']
203+
})
204+
})
205+
206+
test('utility type: Omit', () => {
207+
expect(
208+
resolve(`
209+
type T = { foo: number, bar: string, baz: boolean }
210+
type K = 'foo' | 'bar'
211+
type Target = Omit<T, K>
212+
`).elements
213+
).toStrictEqual({
214+
baz: ['Boolean']
215+
})
216+
})
217+
193218
describe('errors', () => {
194219
test('error on computed keys', () => {
195220
expect(() => resolve(`type Target = { [Foo]: string }`)).toThrow(

packages/compiler-sfc/src/script/resolveType.ts

+72-7
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,18 @@ function innerResolveTypeElements(
7272
if (resolved) {
7373
return resolveTypeElements(ctx, resolved)
7474
} else {
75-
// TODO Pick / Omit
76-
ctx.error(`Failed to resolved type reference`, node)
75+
const typeName = getReferenceName(node)
76+
if (
77+
typeof typeName === 'string' &&
78+
// @ts-ignore
79+
SupportedBuiltinsSet.has(typeName)
80+
) {
81+
return resolveBuiltin(ctx, node, typeName as any)
82+
}
83+
ctx.error(
84+
`Failed to resolved type reference, or unsupported built-in utlility type.`,
85+
node
86+
)
7787
}
7888
}
7989
case 'TSUnionType':
@@ -290,24 +300,79 @@ function resolveTemplateKeys(
290300
return res
291301
}
292302

303+
const SupportedBuiltinsSet = new Set([
304+
'Partial',
305+
'Required',
306+
'Readonly',
307+
'Pick',
308+
'Omit'
309+
] as const)
310+
311+
type GetSetType<T> = T extends Set<infer V> ? V : never
312+
313+
function resolveBuiltin(
314+
ctx: ScriptCompileContext,
315+
node: TSTypeReference | TSExpressionWithTypeArguments,
316+
name: GetSetType<typeof SupportedBuiltinsSet>
317+
): ResolvedElements {
318+
const t = resolveTypeElements(ctx, node.typeParameters!.params[0])
319+
switch (name) {
320+
case 'Partial':
321+
case 'Required':
322+
case 'Readonly':
323+
return t
324+
case 'Pick': {
325+
const picked = resolveStringType(ctx, node.typeParameters!.params[1])
326+
const res: ResolvedElements = {}
327+
if (t.__callSignatures) addCallSignature(res, t.__callSignatures)
328+
for (const key of picked) {
329+
res[key] = t[key]
330+
}
331+
return res
332+
}
333+
case 'Omit':
334+
const omitted = resolveStringType(ctx, node.typeParameters!.params[1])
335+
const res: ResolvedElements = {}
336+
if (t.__callSignatures) addCallSignature(res, t.__callSignatures)
337+
for (const key in t) {
338+
if (!omitted.includes(key)) {
339+
res[key] = t[key]
340+
}
341+
}
342+
return res
343+
}
344+
}
345+
293346
function resolveTypeReference(
294347
ctx: ScriptCompileContext,
295348
node: TSTypeReference | TSExpressionWithTypeArguments,
296349
scope = getRootScope(ctx)
297350
): Node | undefined {
298-
const ref = node.type === 'TSTypeReference' ? node.typeName : node.expression
299-
if (ref.type === 'Identifier') {
300-
if (scope.imports[ref.name]) {
351+
const name = getReferenceName(node)
352+
if (typeof name === 'string') {
353+
if (scope.imports[name]) {
301354
// TODO external import
302-
} else if (scope.types[ref.name]) {
303-
return scope.types[ref.name]
355+
} else if (scope.types[name]) {
356+
return scope.types[name]
304357
}
305358
} else {
306359
// TODO qualified name, e.g. Foo.Bar
307360
// return resolveTypeReference()
308361
}
309362
}
310363

364+
function getReferenceName(
365+
node: TSTypeReference | TSExpressionWithTypeArguments
366+
): string | string[] {
367+
const ref = node.type === 'TSTypeReference' ? node.typeName : node.expression
368+
if (ref.type === 'Identifier') {
369+
return ref.name
370+
} else {
371+
// TODO qualified name, e.g. Foo.Bar
372+
return []
373+
}
374+
}
375+
311376
function getRootScope(ctx: ScriptCompileContext): TypeScope {
312377
if (ctx.scope) {
313378
return ctx.scope

0 commit comments

Comments
 (0)