Skip to content

Commit b71f801

Browse files
committed
fix(compiler-sfc): handle keyof operator
1 parent c0c9432 commit b71f801

File tree

2 files changed

+82
-11
lines changed

2 files changed

+82
-11
lines changed

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

+36
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,42 @@ describe('resolveType', () => {
265265
})
266266
})
267267

268+
test('utility type: keyof', () => {
269+
const files = {
270+
'/foo.ts': `export type IMP = { ${1}: 1 };`,
271+
}
272+
273+
const { props } = resolve(
274+
`
275+
import { IMP } from './foo'
276+
interface Foo { foo: 1, ${1}: 1 }
277+
type Bar = { bar: 1 }
278+
declare const obj: Bar
279+
declare const set: Set<any>
280+
declare const arr: Array<any>
281+
282+
defineProps<{
283+
imp: keyof IMP,
284+
foo: keyof Foo,
285+
bar: keyof Bar,
286+
obj: keyof typeof obj,
287+
set: keyof typeof set,
288+
arr: keyof typeof arr
289+
}>()
290+
`,
291+
files,
292+
)
293+
294+
expect(props).toStrictEqual({
295+
imp: ['Number'],
296+
foo: ['String', 'Number'],
297+
bar: ['String'],
298+
obj: ['String'],
299+
set: ['String'],
300+
arr: ['String', 'Number'],
301+
})
302+
})
303+
268304
test('utility type: ReadonlyArray', () => {
269305
expect(
270306
resolve(`

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

+46-11
Original file line numberDiff line numberDiff line change
@@ -1448,7 +1448,10 @@ export function inferRuntimeType(
14481448
ctx: TypeResolveContext,
14491449
node: Node & MaybeWithScope,
14501450
scope = node._ownerScope || ctxToScope(ctx),
1451+
options: { isKeyof?: boolean } = {},
14511452
): string[] {
1453+
const { isKeyof } = options
1454+
14521455
try {
14531456
switch (node.type) {
14541457
case 'TSStringKeyword':
@@ -1467,16 +1470,33 @@ export function inferRuntimeType(
14671470
const types = new Set<string>()
14681471
const members =
14691472
node.type === 'TSTypeLiteral' ? node.members : node.body.body
1473+
1474+
const handler = isKeyof
1475+
? (m: TSTypeElement) => {
1476+
if (
1477+
m.type === 'TSPropertySignature' &&
1478+
m.key.type === 'NumericLiteral'
1479+
) {
1480+
types.add('Number')
1481+
} else {
1482+
types.add('String')
1483+
}
1484+
}
1485+
: (m: TSTypeElement) => {
1486+
if (
1487+
m.type === 'TSCallSignatureDeclaration' ||
1488+
m.type === 'TSConstructSignatureDeclaration'
1489+
) {
1490+
types.add('Function')
1491+
} else {
1492+
types.add('Object')
1493+
}
1494+
}
1495+
14701496
for (const m of members) {
1471-
if (
1472-
m.type === 'TSCallSignatureDeclaration' ||
1473-
m.type === 'TSConstructSignatureDeclaration'
1474-
) {
1475-
types.add('Function')
1476-
} else {
1477-
types.add('Object')
1478-
}
1497+
handler(m)
14791498
}
1499+
14801500
return types.size ? Array.from(types) : ['Object']
14811501
}
14821502
case 'TSPropertySignature':
@@ -1512,9 +1532,22 @@ export function inferRuntimeType(
15121532
case 'TSTypeReference': {
15131533
const resolved = resolveTypeReference(ctx, node, scope)
15141534
if (resolved) {
1515-
return inferRuntimeType(ctx, resolved, resolved._ownerScope)
1535+
return inferRuntimeType(ctx, resolved, resolved._ownerScope, options)
15161536
}
1537+
15171538
if (node.typeName.type === 'Identifier') {
1539+
if (isKeyof) {
1540+
switch (node.typeName.name) {
1541+
case 'String':
1542+
case 'Array':
1543+
case 'ArrayLike':
1544+
case 'ReadonlyArray':
1545+
return ['String', 'Number']
1546+
default:
1547+
return ['String']
1548+
}
1549+
}
1550+
15181551
switch (node.typeName.name) {
15191552
case 'Array':
15201553
case 'Function':
@@ -1634,15 +1667,17 @@ export function inferRuntimeType(
16341667
// typeof only support identifier in local scope
16351668
const matched = scope.declares[id.name]
16361669
if (matched) {
1637-
return inferRuntimeType(ctx, matched, matched._ownerScope)
1670+
return inferRuntimeType(ctx, matched, matched._ownerScope, options)
16381671
}
16391672
}
16401673
break
16411674
}
16421675

16431676
// e.g. readonly
16441677
case 'TSTypeOperator': {
1645-
return inferRuntimeType(ctx, node.typeAnnotation, scope)
1678+
return inferRuntimeType(ctx, node.typeAnnotation, scope, {
1679+
isKeyof: node.operator === 'keyof',
1680+
})
16461681
}
16471682
}
16481683
} catch (e) {

0 commit comments

Comments
 (0)