Skip to content

Commit 10d34a5

Browse files
authored
fix(compiler-sfc): handle keyof operator (#10874)
close #10871
1 parent 8366126 commit 10d34a5

File tree

2 files changed

+70
-4
lines changed

2 files changed

+70
-4
lines changed

Diff for: packages/compiler-sfc/__tests__/compileScript/resolveType.spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,42 @@ describe('resolveType', () => {
447447
})
448448
})
449449

450+
test('keyof', () => {
451+
const files = {
452+
'/foo.ts': `export type IMP = { ${1}: 1 };`,
453+
}
454+
455+
const { props } = resolve(
456+
`
457+
import { IMP } from './foo'
458+
interface Foo { foo: 1, ${1}: 1 }
459+
type Bar = { bar: 1 }
460+
declare const obj: Bar
461+
declare const set: Set<any>
462+
declare const arr: Array<any>
463+
464+
defineProps<{
465+
imp: keyof IMP,
466+
foo: keyof Foo,
467+
bar: keyof Bar,
468+
obj: keyof typeof obj,
469+
set: keyof typeof set,
470+
arr: keyof typeof arr
471+
}>()
472+
`,
473+
files,
474+
)
475+
476+
expect(props).toStrictEqual({
477+
imp: ['Number'],
478+
foo: ['String', 'Number'],
479+
bar: ['String'],
480+
obj: ['String'],
481+
set: ['String'],
482+
arr: ['String', 'Number'],
483+
})
484+
})
485+
450486
test('ExtractPropTypes (element-plus)', () => {
451487
const { props, raw } = resolve(
452488
`

Diff for: packages/compiler-sfc/src/script/resolveType.ts

+34-4
Original file line numberDiff line numberDiff line change
@@ -1448,6 +1448,7 @@ export function inferRuntimeType(
14481448
ctx: TypeResolveContext,
14491449
node: Node & MaybeWithScope,
14501450
scope = node._ownerScope || ctxToScope(ctx),
1451+
isKeyOf = false,
14511452
): string[] {
14521453
try {
14531454
switch (node.type) {
@@ -1467,8 +1468,18 @@ export function inferRuntimeType(
14671468
const types = new Set<string>()
14681469
const members =
14691470
node.type === 'TSTypeLiteral' ? node.members : node.body.body
1471+
14701472
for (const m of members) {
1471-
if (
1473+
if (isKeyOf) {
1474+
if (
1475+
m.type === 'TSPropertySignature' &&
1476+
m.key.type === 'NumericLiteral'
1477+
) {
1478+
types.add('Number')
1479+
} else {
1480+
types.add('String')
1481+
}
1482+
} else if (
14721483
m.type === 'TSCallSignatureDeclaration' ||
14731484
m.type === 'TSConstructSignatureDeclaration'
14741485
) {
@@ -1477,6 +1488,7 @@ export function inferRuntimeType(
14771488
types.add('Object')
14781489
}
14791490
}
1491+
14801492
return types.size ? Array.from(types) : ['Object']
14811493
}
14821494
case 'TSPropertySignature':
@@ -1512,9 +1524,22 @@ export function inferRuntimeType(
15121524
case 'TSTypeReference': {
15131525
const resolved = resolveTypeReference(ctx, node, scope)
15141526
if (resolved) {
1515-
return inferRuntimeType(ctx, resolved, resolved._ownerScope)
1527+
return inferRuntimeType(ctx, resolved, resolved._ownerScope, isKeyOf)
15161528
}
1529+
15171530
if (node.typeName.type === 'Identifier') {
1531+
if (isKeyOf) {
1532+
switch (node.typeName.name) {
1533+
case 'String':
1534+
case 'Array':
1535+
case 'ArrayLike':
1536+
case 'ReadonlyArray':
1537+
return ['String', 'Number']
1538+
default:
1539+
return ['String']
1540+
}
1541+
}
1542+
15181543
switch (node.typeName.name) {
15191544
case 'Array':
15201545
case 'Function':
@@ -1634,15 +1659,20 @@ export function inferRuntimeType(
16341659
// typeof only support identifier in local scope
16351660
const matched = scope.declares[id.name]
16361661
if (matched) {
1637-
return inferRuntimeType(ctx, matched, matched._ownerScope)
1662+
return inferRuntimeType(ctx, matched, matched._ownerScope, isKeyOf)
16381663
}
16391664
}
16401665
break
16411666
}
16421667

16431668
// e.g. readonly
16441669
case 'TSTypeOperator': {
1645-
return inferRuntimeType(ctx, node.typeAnnotation, scope)
1670+
return inferRuntimeType(
1671+
ctx,
1672+
node.typeAnnotation,
1673+
scope,
1674+
node.operator === 'keyof',
1675+
)
16461676
}
16471677
}
16481678
} catch (e) {

0 commit comments

Comments
 (0)