Skip to content

Commit 3ef1b56

Browse files
authored
Merge pull request microsoft#21496 from Microsoft/inferTypes
Type inference in conditional types
2 parents c4485bc + 4ae8445 commit 3ef1b56

25 files changed

+1565
-409
lines changed

src/compiler/binder.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ namespace ts {
101101
HasLocals = 1 << 5,
102102
IsInterface = 1 << 6,
103103
IsObjectLiteralOrClassExpressionMethod = 1 << 7,
104+
IsInferenceContainer = 1 << 8,
104105
}
105106

106107
const binder = createBinder();
@@ -119,6 +120,7 @@ namespace ts {
119120
let parent: Node;
120121
let container: Node;
121122
let blockScopeContainer: Node;
123+
let inferenceContainer: Node;
122124
let lastContainer: Node;
123125
let seenThisKeyword: boolean;
124126

@@ -186,6 +188,7 @@ namespace ts {
186188
parent = undefined;
187189
container = undefined;
188190
blockScopeContainer = undefined;
191+
inferenceContainer = undefined;
189192
lastContainer = undefined;
190193
seenThisKeyword = false;
191194
currentFlow = undefined;
@@ -561,6 +564,13 @@ namespace ts {
561564
bindChildren(node);
562565
node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis;
563566
}
567+
else if (containerFlags & ContainerFlags.IsInferenceContainer) {
568+
const saveInferenceContainer = inferenceContainer;
569+
inferenceContainer = node;
570+
node.locals = undefined;
571+
bindChildren(node);
572+
inferenceContainer = saveInferenceContainer;
573+
}
564574
else {
565575
bindChildren(node);
566576
}
@@ -1417,6 +1427,9 @@ namespace ts {
14171427
case SyntaxKind.MappedType:
14181428
return ContainerFlags.IsContainer | ContainerFlags.HasLocals;
14191429

1430+
case SyntaxKind.ConditionalType:
1431+
return ContainerFlags.IsInferenceContainer;
1432+
14201433
case SyntaxKind.SourceFile:
14211434
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals;
14221435

@@ -2059,7 +2072,7 @@ namespace ts {
20592072
case SyntaxKind.TypePredicate:
20602073
return checkTypePredicate(node as TypePredicateNode);
20612074
case SyntaxKind.TypeParameter:
2062-
return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
2075+
return bindTypeParameter(node as TypeParameterDeclaration);
20632076
case SyntaxKind.Parameter:
20642077
return bindParameter(<ParameterDeclaration>node);
20652078
case SyntaxKind.VariableDeclaration:
@@ -2576,6 +2589,23 @@ namespace ts {
25762589
: declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
25772590
}
25782591

2592+
function bindTypeParameter(node: TypeParameterDeclaration) {
2593+
if (node.parent.kind === SyntaxKind.InferType) {
2594+
if (inferenceContainer) {
2595+
if (!inferenceContainer.locals) {
2596+
inferenceContainer.locals = createSymbolTable();
2597+
}
2598+
declareSymbol(inferenceContainer.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
2599+
}
2600+
else {
2601+
bindAnonymousDeclaration(node, SymbolFlags.TypeParameter, getDeclarationName(node));
2602+
}
2603+
}
2604+
else {
2605+
declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
2606+
}
2607+
}
2608+
25792609
// reachability checks
25802610

25812611
function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean {
@@ -3441,6 +3471,7 @@ namespace ts {
34413471
case SyntaxKind.UnionType:
34423472
case SyntaxKind.IntersectionType:
34433473
case SyntaxKind.ConditionalType:
3474+
case SyntaxKind.InferType:
34443475
case SyntaxKind.ParenthesizedType:
34453476
case SyntaxKind.InterfaceDeclaration:
34463477
case SyntaxKind.TypeAliasDeclaration:

src/compiler/checker.ts

Lines changed: 115 additions & 38 deletions
Large diffs are not rendered by default.

src/compiler/declarationEmitter.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,8 @@ namespace ts {
452452
return emitIntersectionType(<IntersectionTypeNode>type);
453453
case SyntaxKind.ConditionalType:
454454
return emitConditionalType(<ConditionalTypeNode>type);
455+
case SyntaxKind.InferType:
456+
return emitInferType(<InferTypeNode>type);
455457
case SyntaxKind.ParenthesizedType:
456458
return emitParenType(<ParenthesizedTypeNode>type);
457459
case SyntaxKind.TypeOperator:
@@ -552,11 +554,19 @@ namespace ts {
552554
write(" extends ");
553555
emitType(node.extendsType);
554556
write(" ? ");
557+
const prevEnclosingDeclaration = enclosingDeclaration;
558+
enclosingDeclaration = node.trueType;
555559
emitType(node.trueType);
560+
enclosingDeclaration = prevEnclosingDeclaration;
556561
write(" : ");
557562
emitType(node.falseType);
558563
}
559564

565+
function emitInferType(node: InferTypeNode) {
566+
write("infer ");
567+
writeTextOfNode(currentText, node.typeParameter.name);
568+
}
569+
560570
function emitParenType(type: ParenthesizedTypeNode) {
561571
write("(");
562572
emitType(type.type);

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,10 @@
947947
"category": "Error",
948948
"code": 1337
949949
},
950+
"'infer' declarations are only permitted in the 'extends' clause of a conditional type.": {
951+
"category": "Error",
952+
"code": 1338
953+
},
950954

951955
"Duplicate identifier '{0}'.": {
952956
"category": "Error",

src/compiler/emitter.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,8 @@ namespace ts {
602602
return emitIntersectionType(<IntersectionTypeNode>node);
603603
case SyntaxKind.ConditionalType:
604604
return emitConditionalType(<ConditionalTypeNode>node);
605+
case SyntaxKind.InferType:
606+
return emitInferType(<InferTypeNode>node);
605607
case SyntaxKind.ParenthesizedType:
606608
return emitParenthesizedType(<ParenthesizedTypeNode>node);
607609
case SyntaxKind.ExpressionWithTypeArguments:
@@ -1194,14 +1196,26 @@ namespace ts {
11941196

11951197
function emitConditionalType(node: ConditionalTypeNode) {
11961198
emit(node.checkType);
1197-
write(" extends ");
1199+
writeSpace();
1200+
writeKeyword("extends");
1201+
writeSpace();
11981202
emit(node.extendsType);
1199-
write(" ? ");
1203+
writeSpace();
1204+
writePunctuation("?");
1205+
writeSpace();
12001206
emit(node.trueType);
1201-
write(" : ");
1207+
writeSpace();
1208+
writePunctuation(":");
1209+
writeSpace();
12021210
emit(node.falseType);
12031211
}
12041212

1213+
function emitInferType(node: InferTypeNode) {
1214+
writeKeyword("infer");
1215+
writeSpace();
1216+
emit(node.typeParameter);
1217+
}
1218+
12051219
function emitParenthesizedType(node: ParenthesizedTypeNode) {
12061220
writePunctuation("(");
12071221
emit(node.type);

src/compiler/factory.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,18 @@ namespace ts {
749749
: node;
750750
}
751751

752+
export function createInferTypeNode(typeParameter: TypeParameterDeclaration) {
753+
const node = <InferTypeNode>createSynthesizedNode(SyntaxKind.InferType);
754+
node.typeParameter = typeParameter;
755+
return node;
756+
}
757+
758+
export function updateInferTypeNode(node: InferTypeNode, typeParameter: TypeParameterDeclaration) {
759+
return node.typeParameter !== typeParameter
760+
? updateNode(createInferTypeNode(typeParameter), node)
761+
: node;
762+
}
763+
752764
export function createParenthesizedType(type: TypeNode) {
753765
const node = <ParenthesizedTypeNode>createSynthesizedNode(SyntaxKind.ParenthesizedType);
754766
node.type = type;

src/compiler/parser.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ namespace ts {
180180
visitNode(cbNode, (<ConditionalTypeNode>node).extendsType) ||
181181
visitNode(cbNode, (<ConditionalTypeNode>node).trueType) ||
182182
visitNode(cbNode, (<ConditionalTypeNode>node).falseType);
183+
case SyntaxKind.InferType:
184+
return visitNode(cbNode, (<InferTypeNode>node).typeParameter);
183185
case SyntaxKind.ParenthesizedType:
184186
case SyntaxKind.TypeOperator:
185187
return visitNode(cbNode, (<ParenthesizedTypeNode | TypeOperatorNode>node).type);
@@ -2767,6 +2769,7 @@ namespace ts {
27672769
case SyntaxKind.QuestionToken:
27682770
case SyntaxKind.ExclamationToken:
27692771
case SyntaxKind.DotDotDotToken:
2772+
case SyntaxKind.InferKeyword:
27702773
return true;
27712774
case SyntaxKind.MinusToken:
27722775
return !inStartOfParameter && lookAhead(nextTokenIsNumericLiteral);
@@ -2843,12 +2846,23 @@ namespace ts {
28432846
return finishNode(node);
28442847
}
28452848

2849+
function parseInferType(): InferTypeNode {
2850+
const node = <InferTypeNode>createNode(SyntaxKind.InferType);
2851+
parseExpected(SyntaxKind.InferKeyword);
2852+
const typeParameter = <TypeParameterDeclaration>createNode(SyntaxKind.TypeParameter);
2853+
typeParameter.name = parseIdentifier();
2854+
node.typeParameter = finishNode(typeParameter);
2855+
return finishNode(node);
2856+
}
2857+
28462858
function parseTypeOperatorOrHigher(): TypeNode {
28472859
const operator = token();
28482860
switch (operator) {
28492861
case SyntaxKind.KeyOfKeyword:
28502862
case SyntaxKind.UniqueKeyword:
28512863
return parseTypeOperator(operator);
2864+
case SyntaxKind.InferKeyword:
2865+
return parseInferType();
28522866
case SyntaxKind.DotDotDotToken: {
28532867
const result = createNode(SyntaxKind.JSDocVariadicType) as JSDocVariadicType;
28542868
nextToken();

src/compiler/scanner.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ namespace ts {
9292
"implements": SyntaxKind.ImplementsKeyword,
9393
"import": SyntaxKind.ImportKeyword,
9494
"in": SyntaxKind.InKeyword,
95+
"infer": SyntaxKind.InferKeyword,
9596
"instanceof": SyntaxKind.InstanceOfKeyword,
9697
"interface": SyntaxKind.InterfaceKeyword,
9798
"is": SyntaxKind.IsKeyword,

src/compiler/types.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ namespace ts {
215215
ConstructorKeyword,
216216
DeclareKeyword,
217217
GetKeyword,
218+
InferKeyword,
218219
IsKeyword,
219220
KeyOfKeyword,
220221
ModuleKeyword,
@@ -266,6 +267,7 @@ namespace ts {
266267
UnionType,
267268
IntersectionType,
268269
ConditionalType,
270+
InferType,
269271
ParenthesizedType,
270272
ThisType,
271273
TypeOperator,
@@ -771,7 +773,7 @@ namespace ts {
771773

772774
export interface TypeParameterDeclaration extends NamedDeclaration {
773775
kind: SyntaxKind.TypeParameter;
774-
parent?: DeclarationWithTypeParameters;
776+
parent?: DeclarationWithTypeParameters | InferTypeNode;
775777
name: Identifier;
776778
constraint?: TypeNode;
777779
default?: TypeNode;
@@ -1125,6 +1127,11 @@ namespace ts {
11251127
falseType: TypeNode;
11261128
}
11271129

1130+
export interface InferTypeNode extends TypeNode {
1131+
kind: SyntaxKind.InferType;
1132+
typeParameter: TypeParameterDeclaration;
1133+
}
1134+
11281135
export interface ParenthesizedTypeNode extends TypeNode {
11291136
kind: SyntaxKind.ParenthesizedType;
11301137
type: TypeNode;
@@ -3556,6 +3563,8 @@ namespace ts {
35563563
pattern?: DestructuringPattern; // Destructuring pattern represented by type (if any)
35573564
aliasSymbol?: Symbol; // Alias associated with type
35583565
aliasTypeArguments?: Type[]; // Alias type arguments (if any)
3566+
/* @internal */
3567+
resolvedAnyInstantiation?: Type; // Instantiation with type parameters mapped to any
35593568
}
35603569

35613570
/* @internal */
@@ -3801,6 +3810,8 @@ namespace ts {
38013810
trueType: Type;
38023811
falseType: Type;
38033812
/* @internal */
3813+
inferTypeParameters: TypeParameter[];
3814+
/* @internal */
38043815
target?: ConditionalType;
38053816
/* @internal */
38063817
mapper?: TypeMapper;
@@ -3876,6 +3887,8 @@ namespace ts {
38763887
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
38773888
MappedType = 1 << 1, // Reverse inference for mapped type
38783889
ReturnType = 1 << 2, // Inference made from return type of generic function
3890+
NoConstraints = 1 << 3, // Don't infer from constraints of instantiable types
3891+
AlwaysStrict = 1 << 4, // Always use strict rules for contravariant inferences
38793892
}
38803893

38813894
export interface InferenceInfo {

src/compiler/utilities.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4625,6 +4625,10 @@ namespace ts {
46254625
return node.kind === SyntaxKind.ConditionalType;
46264626
}
46274627

4628+
export function isInferTypeNode(node: Node): node is InferTypeNode {
4629+
return node.kind === SyntaxKind.InferType;
4630+
}
4631+
46284632
export function isParenthesizedTypeNode(node: Node): node is ParenthesizedTypeNode {
46294633
return node.kind === SyntaxKind.ParenthesizedType;
46304634
}

src/compiler/visitor.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,10 @@ namespace ts {
394394
visitNode((<ConditionalTypeNode>node).trueType, visitor, isTypeNode),
395395
visitNode((<ConditionalTypeNode>node).falseType, visitor, isTypeNode));
396396

397+
case SyntaxKind.InferType:
398+
return updateInferTypeNode(<InferTypeNode>node,
399+
visitNode((<InferTypeNode>node).typeParameter, visitor, isTypeParameterDeclaration));
400+
397401
case SyntaxKind.ParenthesizedType:
398402
return updateParenthesizedType(<ParenthesizedTypeNode>node,
399403
visitNode((<ParenthesizedTypeNode>node).type, visitor, isTypeNode));

0 commit comments

Comments
 (0)