Skip to content

Commit 7b34b61

Browse files
authored
Merge pull request microsoft#11929 from Microsoft/keyoftypes
Static types for dynamically named properties
2 parents 6b94bae + 4bbe29a commit 7b34b61

File tree

108 files changed

+3568
-1278
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+3568
-1278
lines changed

src/compiler/binder.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,9 +1209,9 @@ namespace ts {
12091209
}
12101210
else {
12111211
forEachChild(node, bind);
1212-
if (operator === SyntaxKind.EqualsToken && !isAssignmentTarget(node)) {
1212+
if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) {
12131213
bindAssignmentTargetFlow(node.left);
1214-
if (node.left.kind === SyntaxKind.ElementAccessExpression) {
1214+
if (operator === SyntaxKind.EqualsToken && node.left.kind === SyntaxKind.ElementAccessExpression) {
12151215
const elementAccess = <ElementAccessExpression>node.left;
12161216
if (isNarrowableOperand(elementAccess.expression)) {
12171217
currentFlow = createFlowArrayMutation(currentFlow, node);
@@ -3097,6 +3097,8 @@ namespace ts {
30973097
case SyntaxKind.InterfaceDeclaration:
30983098
case SyntaxKind.TypeAliasDeclaration:
30993099
case SyntaxKind.ThisType:
3100+
case SyntaxKind.TypeOperator:
3101+
case SyntaxKind.IndexedAccessType:
31003102
case SyntaxKind.LiteralType:
31013103
// Types and signatures are TypeScript syntax, and exclude all other facts.
31023104
transformFlags = TransformFlags.AssertTypeScript;

src/compiler/checker.ts

Lines changed: 244 additions & 192 deletions
Large diffs are not rendered by default.

src/compiler/declarationEmitter.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,10 @@ namespace ts {
413413
return emitIntersectionType(<IntersectionTypeNode>type);
414414
case SyntaxKind.ParenthesizedType:
415415
return emitParenType(<ParenthesizedTypeNode>type);
416+
case SyntaxKind.TypeOperator:
417+
return emitTypeOperator(<TypeOperatorNode>type);
418+
case SyntaxKind.IndexedAccessType:
419+
return emitPropertyAccessType(<IndexedAccessTypeNode>type);
416420
case SyntaxKind.FunctionType:
417421
case SyntaxKind.ConstructorType:
418422
return emitSignatureDeclarationWithJsDocComments(<FunctionOrConstructorTypeNode>type);
@@ -506,6 +510,19 @@ namespace ts {
506510
write(")");
507511
}
508512

513+
function emitTypeOperator(type: TypeOperatorNode) {
514+
write(tokenToString(type.operator));
515+
write(" ");
516+
emitType(type.type);
517+
}
518+
519+
function emitPropertyAccessType(node: IndexedAccessTypeNode) {
520+
emitType(node.objectType);
521+
write("[");
522+
emitType(node.indexType);
523+
write("]");
524+
}
525+
509526
function emitTypeLiteral(type: TypeLiteralNode) {
510527
write("{");
511528
if (type.members.length) {

src/compiler/diagnosticMessages.json

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,7 @@
10711071
"category": "Error",
10721072
"code": 2356
10731073
},
1074-
"The operand of an increment or decrement operator must be a variable, property or indexer.": {
1074+
"The operand of an increment or decrement operator must be a variable or a property access.": {
10751075
"category": "Error",
10761076
"code": 2357
10771077
},
@@ -1099,7 +1099,7 @@
10991099
"category": "Error",
11001100
"code": 2363
11011101
},
1102-
"Invalid left-hand side of assignment expression.": {
1102+
"The left-hand side of an assignment expression must be a variable or a property access.": {
11031103
"category": "Error",
11041104
"code": 2364
11051105
},
@@ -1259,7 +1259,7 @@
12591259
"category": "Error",
12601260
"code": 2405
12611261
},
1262-
"Invalid left-hand side in 'for...in' statement.": {
1262+
"The left-hand side of a 'for...in' statement must be a variable or a property access.": {
12631263
"category": "Error",
12641264
"code": 2406
12651265
},
@@ -1411,14 +1411,6 @@
14111411
"category": "Error",
14121412
"code": 2448
14131413
},
1414-
"The operand of an increment or decrement operator cannot be a constant or a read-only property.": {
1415-
"category": "Error",
1416-
"code": 2449
1417-
},
1418-
"Left-hand side of assignment expression cannot be a constant or a read-only property.": {
1419-
"category": "Error",
1420-
"code": 2450
1421-
},
14221414
"Cannot redeclare block-scoped variable '{0}'.": {
14231415
"category": "Error",
14241416
"code": 2451
@@ -1551,15 +1543,7 @@
15511543
"category": "Error",
15521544
"code": 2484
15531545
},
1554-
"The left-hand side of a 'for...of' statement cannot be a constant or a read-only property.": {
1555-
"category": "Error",
1556-
"code": 2485
1557-
},
1558-
"The left-hand side of a 'for...in' statement cannot be a constant or a read-only property.": {
1559-
"category": "Error",
1560-
"code": 2486
1561-
},
1562-
"Invalid left-hand side in 'for...of' statement.": {
1546+
"The left-hand side of a 'for...of' statement must be a variable or a property access.": {
15631547
"category": "Error",
15641548
"code": 2487
15651549
},
@@ -1747,6 +1731,34 @@
17471731
"category": "Error",
17481732
"code": 2535
17491733
},
1734+
"Type '{0}' is not constrained to 'keyof {1}'.": {
1735+
"category": "Error",
1736+
"code": 2536
1737+
},
1738+
"Type '{0}' has no matching index signature for type '{1}'.": {
1739+
"category": "Error",
1740+
"code": 2537
1741+
},
1742+
"Type '{0}' cannot be used as an index type.": {
1743+
"category": "Error",
1744+
"code": 2538
1745+
},
1746+
"Cannot assign to '{0}' because it is not a variable.": {
1747+
"category": "Error",
1748+
"code": 2539
1749+
},
1750+
"Cannot assign to '{0}' because it is a constant or a read-only property.": {
1751+
"category": "Error",
1752+
"code": 2540
1753+
},
1754+
"The target of an assignment must be a variable or a property access.": {
1755+
"category": "Error",
1756+
"code": 2541
1757+
},
1758+
"Index signature in type '{0}' only permits reading.": {
1759+
"category": "Error",
1760+
"code": 2542
1761+
},
17501762
"JSX element attributes type '{0}' may not be a union type.": {
17511763
"category": "Error",
17521764
"code": 2600
@@ -2913,7 +2925,7 @@
29132925
"category": "Error",
29142926
"code": 7016
29152927
},
2916-
"Index signature of object type implicitly has an 'any' type.": {
2928+
"Element implicitly has an 'any' type because type '{0}' has no index signature.": {
29172929
"category": "Error",
29182930
"code": 7017
29192931
},

src/compiler/emitter.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,10 @@ const _super = (function (geti, seti) {
594594
return emitExpressionWithTypeArguments(<ExpressionWithTypeArguments>node);
595595
case SyntaxKind.ThisType:
596596
return emitThisType();
597+
case SyntaxKind.TypeOperator:
598+
return emitTypeOperator(<TypeOperatorNode>node);
599+
case SyntaxKind.IndexedAccessType:
600+
return emitPropertyAccessType(<IndexedAccessTypeNode>node);
597601
case SyntaxKind.LiteralType:
598602
return emitLiteralType(<LiteralTypeNode>node);
599603

@@ -1088,6 +1092,19 @@ const _super = (function (geti, seti) {
10881092
write("this");
10891093
}
10901094

1095+
function emitTypeOperator(node: TypeOperatorNode) {
1096+
writeTokenText(node.operator);
1097+
write(" ");
1098+
emit(node.type);
1099+
}
1100+
1101+
function emitPropertyAccessType(node: IndexedAccessTypeNode) {
1102+
emit(node.objectType);
1103+
write("[");
1104+
emit(node.indexType);
1105+
write("]");
1106+
}
1107+
10911108
function emitLiteralType(node: LiteralTypeNode) {
10921109
emitExpression(node.literal);
10931110
}

src/compiler/parser.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ namespace ts {
134134
case SyntaxKind.IntersectionType:
135135
return visitNodes(cbNodes, (<UnionOrIntersectionTypeNode>node).types);
136136
case SyntaxKind.ParenthesizedType:
137-
return visitNode(cbNode, (<ParenthesizedTypeNode>node).type);
137+
case SyntaxKind.TypeOperator:
138+
return visitNode(cbNode, (<ParenthesizedTypeNode | TypeOperatorNode>node).type);
139+
case SyntaxKind.IndexedAccessType:
140+
return visitNode(cbNode, (<IndexedAccessTypeNode>node).objectType) ||
141+
visitNode(cbNode, (<IndexedAccessTypeNode>node).indexType);
138142
case SyntaxKind.LiteralType:
139143
return visitNode(cbNode, (<LiteralTypeNode>node).literal);
140144
case SyntaxKind.ObjectBindingPattern:
@@ -2519,14 +2523,39 @@ namespace ts {
25192523
function parseArrayTypeOrHigher(): TypeNode {
25202524
let type = parseNonArrayType();
25212525
while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) {
2522-
parseExpected(SyntaxKind.CloseBracketToken);
2523-
const node = <ArrayTypeNode>createNode(SyntaxKind.ArrayType, type.pos);
2524-
node.elementType = type;
2525-
type = finishNode(node);
2526+
if (isStartOfType()) {
2527+
const node = <IndexedAccessTypeNode>createNode(SyntaxKind.IndexedAccessType, type.pos);
2528+
node.objectType = type;
2529+
node.indexType = parseType();
2530+
parseExpected(SyntaxKind.CloseBracketToken);
2531+
type = finishNode(node);
2532+
}
2533+
else {
2534+
const node = <ArrayTypeNode>createNode(SyntaxKind.ArrayType, type.pos);
2535+
node.elementType = type;
2536+
parseExpected(SyntaxKind.CloseBracketToken);
2537+
type = finishNode(node);
2538+
}
25262539
}
25272540
return type;
25282541
}
25292542

2543+
function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword) {
2544+
const node = <TypeOperatorNode>createNode(SyntaxKind.TypeOperator);
2545+
parseExpected(operator);
2546+
node.operator = operator;
2547+
node.type = parseTypeOperatorOrHigher();
2548+
return finishNode(node);
2549+
}
2550+
2551+
function parseTypeOperatorOrHigher(): TypeNode {
2552+
switch (token()) {
2553+
case SyntaxKind.KeyOfKeyword:
2554+
return parseTypeOperator(SyntaxKind.KeyOfKeyword);
2555+
}
2556+
return parseArrayTypeOrHigher();
2557+
}
2558+
25302559
function parseUnionOrIntersectionType(kind: SyntaxKind, parseConstituentType: () => TypeNode, operator: SyntaxKind): TypeNode {
25312560
let type = parseConstituentType();
25322561
if (token() === operator) {
@@ -2543,7 +2572,7 @@ namespace ts {
25432572
}
25442573

25452574
function parseIntersectionTypeOrHigher(): TypeNode {
2546-
return parseUnionOrIntersectionType(SyntaxKind.IntersectionType, parseArrayTypeOrHigher, SyntaxKind.AmpersandToken);
2575+
return parseUnionOrIntersectionType(SyntaxKind.IntersectionType, parseTypeOperatorOrHigher, SyntaxKind.AmpersandToken);
25472576
}
25482577

25492578
function parseUnionTypeOrHigher(): TypeNode {

src/compiler/scanner.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ namespace ts {
9090
"instanceof": SyntaxKind.InstanceOfKeyword,
9191
"interface": SyntaxKind.InterfaceKeyword,
9292
"is": SyntaxKind.IsKeyword,
93+
"keyof": SyntaxKind.KeyOfKeyword,
9394
"let": SyntaxKind.LetKeyword,
9495
"module": SyntaxKind.ModuleKeyword,
9596
"namespace": SyntaxKind.NamespaceKeyword,

src/compiler/transformers/ts.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ namespace ts {
300300
case SyntaxKind.IntersectionType:
301301
case SyntaxKind.ParenthesizedType:
302302
case SyntaxKind.ThisType:
303+
case SyntaxKind.TypeOperator:
304+
case SyntaxKind.IndexedAccessType:
303305
case SyntaxKind.LiteralType:
304306
// TypeScript type nodes are elided.
305307

@@ -1783,6 +1785,8 @@ namespace ts {
17831785
}
17841786
// Fallthrough
17851787
case SyntaxKind.TypeQuery:
1788+
case SyntaxKind.TypeOperator:
1789+
case SyntaxKind.IndexedAccessType:
17861790
case SyntaxKind.TypeLiteral:
17871791
case SyntaxKind.AnyKeyword:
17881792
case SyntaxKind.ThisType:

src/compiler/types.ts

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ namespace ts {
168168
DeclareKeyword,
169169
GetKeyword,
170170
IsKeyword,
171+
KeyOfKeyword,
171172
ModuleKeyword,
172173
NamespaceKeyword,
173174
NeverKeyword,
@@ -216,6 +217,8 @@ namespace ts {
216217
IntersectionType,
217218
ParenthesizedType,
218219
ThisType,
220+
TypeOperator,
221+
IndexedAccessType,
219222
LiteralType,
220223
// Binding patterns
221224
ObjectBindingPattern,
@@ -882,6 +885,18 @@ namespace ts {
882885
type: TypeNode;
883886
}
884887

888+
export interface TypeOperatorNode extends TypeNode {
889+
kind: SyntaxKind.TypeOperator;
890+
operator: SyntaxKind.KeyOfKeyword;
891+
type: TypeNode;
892+
}
893+
894+
export interface IndexedAccessTypeNode extends TypeNode {
895+
kind: SyntaxKind.IndexedAccessType;
896+
objectType: TypeNode;
897+
indexType: TypeNode;
898+
}
899+
885900
export interface LiteralTypeNode extends TypeNode {
886901
kind: SyntaxKind.LiteralType;
887902
literal: Expression;
@@ -953,10 +968,6 @@ namespace ts {
953968
operator: PostfixUnaryOperator;
954969
}
955970

956-
export interface PostfixExpression extends UnaryExpression {
957-
_postfixExpressionBrand: any;
958-
}
959-
960971
export interface LeftHandSideExpression extends IncrementExpression {
961972
_leftHandSideExpressionBrand: any;
962973
}
@@ -2667,14 +2678,16 @@ namespace ts {
26672678
Object = 1 << 15, // Object type
26682679
Union = 1 << 16, // Union (T | U)
26692680
Intersection = 1 << 17, // Intersection (T & U)
2681+
Index = 1 << 18, // keyof T
2682+
IndexedAccess = 1 << 19, // T[K]
26702683
/* @internal */
2671-
FreshLiteral = 1 << 18, // Fresh literal type
2684+
FreshLiteral = 1 << 20, // Fresh literal type
26722685
/* @internal */
2673-
ContainsWideningType = 1 << 19, // Type is or contains undefined or null widening type
2686+
ContainsWideningType = 1 << 21, // Type is or contains undefined or null widening type
26742687
/* @internal */
2675-
ContainsObjectLiteral = 1 << 20, // Type is or contains object literal type
2688+
ContainsObjectLiteral = 1 << 22, // Type is or contains object literal type
26762689
/* @internal */
2677-
ContainsAnyFunctionType = 1 << 21, // Type is or contains object literal type
2690+
ContainsAnyFunctionType = 1 << 23, // Type is or contains object literal type
26782691

26792692
/* @internal */
26802693
Nullable = Undefined | Null,
@@ -2697,7 +2710,7 @@ namespace ts {
26972710

26982711
// 'Narrowable' types are types where narrowing actually narrows.
26992712
// This *should* be every type other than null, undefined, void, and never
2700-
Narrowable = Any | StructuredType | TypeParameter | StringLike | NumberLike | BooleanLike | ESSymbol,
2713+
Narrowable = Any | StructuredType | TypeParameter | Index | IndexedAccess | StringLike | NumberLike | BooleanLike | ESSymbol,
27012714
NotUnionOrUnit = Any | ESSymbol | Object,
27022715
/* @internal */
27032716
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
@@ -2860,9 +2873,22 @@ namespace ts {
28602873
/* @internal */
28612874
resolvedApparentType: Type;
28622875
/* @internal */
2876+
resolvedIndexType: IndexType;
2877+
/* @internal */
2878+
resolvedIndexedAccessTypes: IndexedAccessType[];
2879+
/* @internal */
28632880
isThisType?: boolean;
28642881
}
28652882

2883+
export interface IndexType extends Type {
2884+
type: TypeParameter;
2885+
}
2886+
2887+
export interface IndexedAccessType extends Type {
2888+
objectType: Type;
2889+
indexType: TypeParameter;
2890+
}
2891+
28662892
export const enum SignatureKind {
28672893
Call,
28682894
Construct,

0 commit comments

Comments
 (0)