Skip to content

Commit 8fd5eda

Browse files
committed
Add 'extends' clause to 'infer' type
1 parent 93c3a30 commit 8fd5eda

23 files changed

+1017
-55
lines changed

src/compiler/checker.ts

+19-3
Original file line numberDiff line numberDiff line change
@@ -35538,6 +35538,22 @@ namespace ts {
3553835538
grammarErrorOnNode(node, Diagnostics.infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type);
3553935539
}
3554035540
checkSourceElement(node.typeParameter);
35541+
const symbol = getSymbolOfNode(node.typeParameter);
35542+
if (symbol.declarations && symbol.declarations.length > 1) {
35543+
const links = getSymbolLinks(symbol);
35544+
if (!links.typeParametersChecked) {
35545+
links.typeParametersChecked = true;
35546+
const typeParameter = getDeclaredTypeOfTypeParameter(symbol);
35547+
const declarations: TypeParameterDeclaration[] = getDeclarationsOfKind(symbol, SyntaxKind.TypeParameter);
35548+
if (!areTypeParametersIdentical(declarations, [typeParameter], decl => [decl])) {
35549+
// Report an error on every conflicting declaration.
35550+
const name = symbolToString(symbol);
35551+
for (const declaration of declarations) {
35552+
error(declaration.name, Diagnostics.All_declarations_of_0_must_have_identical_constraints, name);
35553+
}
35554+
}
35555+
}
35556+
}
3554135557
registerForUnusedIdentifiersCheck(node);
3554235558
}
3554335559

@@ -39014,7 +39030,7 @@ namespace ts {
3901439030
}
3901539031

3901639032
const type = getDeclaredTypeOfSymbol(symbol) as InterfaceType;
39017-
if (!areTypeParametersIdentical(declarations, type.localTypeParameters!)) {
39033+
if (!areTypeParametersIdentical(declarations, type.localTypeParameters!, getEffectiveTypeParameterDeclarations)) {
3901839034
// Report an error on every conflicting declaration.
3901939035
const name = symbolToString(symbol);
3902039036
for (const declaration of declarations) {
@@ -39024,13 +39040,13 @@ namespace ts {
3902439040
}
3902539041
}
3902639042

39027-
function areTypeParametersIdentical(declarations: readonly (ClassDeclaration | InterfaceDeclaration)[], targetParameters: TypeParameter[]) {
39043+
function areTypeParametersIdentical<T extends DeclarationWithTypeParameters | TypeParameterDeclaration>(declarations: readonly T[], targetParameters: TypeParameter[], getTypeParameterDeclarations: (node: T) => readonly TypeParameterDeclaration[]) {
3902839044
const maxTypeArgumentCount = length(targetParameters);
3902939045
const minTypeArgumentCount = getMinTypeArgumentCount(targetParameters);
3903039046

3903139047
for (const declaration of declarations) {
3903239048
// If this declaration has too few or too many type parameters, we report an error
39033-
const sourceParameters = getEffectiveTypeParameterDeclarations(declaration);
39049+
const sourceParameters = getTypeParameterDeclarations(declaration);
3903439050
const numTypeParameters = sourceParameters.length;
3903539051
if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) {
3903639052
return false;

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -3413,6 +3413,10 @@
34133413
"category": "Error",
34143414
"code": 2837
34153415
},
3416+
"All declarations of '{0}' must have identical constraints.": {
3417+
"category": "Error",
3418+
"code": 2838
3419+
},
34163420

34173421
"Import declaration '{0}' is using private name '{1}'.": {
34183422
"category": "Error",

src/compiler/factory/nodeFactory.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1989,7 +1989,7 @@ namespace ts {
19891989
// @api
19901990
function createConditionalTypeNode(checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode) {
19911991
const node = createBaseNode<ConditionalTypeNode>(SyntaxKind.ConditionalType);
1992-
node.checkType = parenthesizerRules().parenthesizeMemberOfConditionalType(checkType);
1992+
node.checkType = parenthesizerRules().parenthesizeCheckTypeOfConditionalType(checkType);
19931993
node.extendsType = parenthesizerRules().parenthesizeMemberOfConditionalType(extendsType);
19941994
node.trueType = trueType;
19951995
node.falseType = falseType;

src/compiler/factory/parenthesizerRules.ts

+7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ namespace ts {
2525
parenthesizeExpressionForDisallowedComma,
2626
parenthesizeExpressionOfExpressionStatement,
2727
parenthesizeConciseBodyOfArrowFunction,
28+
parenthesizeCheckTypeOfConditionalType,
2829
parenthesizeMemberOfConditionalType,
2930
parenthesizeMemberOfElementType,
3031
parenthesizeElementTypeOfArrayType,
@@ -388,6 +389,11 @@ namespace ts {
388389
return body;
389390
}
390391

392+
function parenthesizeCheckTypeOfConditionalType(member: TypeNode): TypeNode {
393+
return isInferTypeNode(member) ? factory.createParenthesizedType(member) :
394+
parenthesizeMemberOfConditionalType(member);
395+
}
396+
391397
function parenthesizeMemberOfConditionalType(member: TypeNode): TypeNode {
392398
return member.kind === SyntaxKind.ConditionalType ? factory.createParenthesizedType(member) : member;
393399
}
@@ -446,6 +452,7 @@ namespace ts {
446452
parenthesizeExpressionForDisallowedComma: identity,
447453
parenthesizeExpressionOfExpressionStatement: identity,
448454
parenthesizeConciseBodyOfArrowFunction: identity,
455+
parenthesizeCheckTypeOfConditionalType: identity,
449456
parenthesizeMemberOfConditionalType: identity,
450457
parenthesizeMemberOfElementType: identity,
451458
parenthesizeElementTypeOfArrayType: identity,

src/compiler/parser.ts

+6-14
Original file line numberDiff line numberDiff line change
@@ -3155,6 +3155,10 @@ namespace ts {
31553155
}
31563156

31573157
function parseTypeParameter(): TypeParameterDeclaration {
3158+
return parseTypeParameterWorker(/*canHaveDefault*/ true);
3159+
}
3160+
3161+
function parseTypeParameterWorker(canHaveDefault: boolean): TypeParameterDeclaration {
31583162
const pos = getNodePos();
31593163
const name = parseIdentifier();
31603164
let constraint: TypeNode | undefined;
@@ -3179,7 +3183,7 @@ namespace ts {
31793183
}
31803184
}
31813185

3182-
const defaultType = parseOptional(SyntaxKind.EqualsToken) ? parseType() : undefined;
3186+
const defaultType = canHaveDefault && parseOptional(SyntaxKind.EqualsToken) ? parseType() : undefined;
31833187
const node = factory.createTypeParameterDeclaration(name, constraint, defaultType);
31843188
node.expression = expression;
31853189
return finishNode(node, pos);
@@ -3937,22 +3941,10 @@ namespace ts {
39373941
return finishNode(factory.createTypeOperatorNode(operator, parseTypeOperatorOrHigher()), pos);
39383942
}
39393943

3940-
function parseTypeParameterOfInferType() {
3941-
const pos = getNodePos();
3942-
return finishNode(
3943-
factory.createTypeParameterDeclaration(
3944-
parseIdentifier(),
3945-
/*constraint*/ undefined,
3946-
/*defaultType*/ undefined
3947-
),
3948-
pos
3949-
);
3950-
}
3951-
39523944
function parseInferType(): InferTypeNode {
39533945
const pos = getNodePos();
39543946
parseExpected(SyntaxKind.InferKeyword);
3955-
return finishNode(factory.createInferTypeNode(parseTypeParameterOfInferType()), pos);
3947+
return finishNode(factory.createInferTypeNode(parseTypeParameterWorker(/*canHaveDefault*/ false)), pos);
39563948
}
39573949

39583950
function parseTypeOperatorOrHigher(): TypeNode {

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7124,6 +7124,7 @@ namespace ts {
71247124
parenthesizeExpressionOfExpressionStatement(expression: Expression): Expression;
71257125
parenthesizeConciseBodyOfArrowFunction(body: Expression): Expression;
71267126
parenthesizeConciseBodyOfArrowFunction(body: ConciseBody): ConciseBody;
7127+
parenthesizeCheckTypeOfConditionalType(member: TypeNode): TypeNode;
71277128
parenthesizeMemberOfConditionalType(member: TypeNode): TypeNode;
71287129
parenthesizeMemberOfElementType(member: TypeNode): TypeNode;
71297130
parenthesizeElementTypeOfArrayType(member: TypeNode): TypeNode;

tests/baselines/reference/inferTypes1.errors.txt

+8-8
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ tests/cases/conformance/types/conditional/inferTypes1.ts(55,25): error TS2344: T
88
tests/cases/conformance/types/conditional/inferTypes1.ts(56,25): error TS2344: Type 'Function' does not satisfy the constraint '(x: any) => any'.
99
Type 'Function' provides no match for the signature '(x: any): any'.
1010
tests/cases/conformance/types/conditional/inferTypes1.ts(82,12): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
11-
tests/cases/conformance/types/conditional/inferTypes1.ts(83,15): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
12-
tests/cases/conformance/types/conditional/inferTypes1.ts(83,41): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
13-
tests/cases/conformance/types/conditional/inferTypes1.ts(83,51): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
11+
tests/cases/conformance/types/conditional/inferTypes1.ts(83,16): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
12+
tests/cases/conformance/types/conditional/inferTypes1.ts(83,43): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
13+
tests/cases/conformance/types/conditional/inferTypes1.ts(83,53): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
1414
tests/cases/conformance/types/conditional/inferTypes1.ts(84,15): error TS2304: Cannot find name 'U'.
1515
tests/cases/conformance/types/conditional/inferTypes1.ts(84,15): error TS4081: Exported type alias 'T62' has or is using private name 'U'.
1616
tests/cases/conformance/types/conditional/inferTypes1.ts(84,43): error TS2304: Cannot find name 'U'.
@@ -120,12 +120,12 @@ tests/cases/conformance/types/conditional/inferTypes1.ts(153,40): error TS2322:
120120
type T60 = infer U; // Error
121121
~~~~~~~
122122
!!! error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
123-
type T61<T> = infer A extends infer B ? infer C : infer D; // Error
124-
~~~~~~~
123+
type T61<T> = (infer A) extends infer B ? infer C : infer D; // Error
124+
~~~~~~~
125125
!!! error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
126-
~~~~~~~
126+
~~~~~~~
127127
!!! error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
128-
~~~~~~~
128+
~~~~~~~
129129
!!! error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.
130130
type T62<T> = U extends (infer U)[] ? U : U; // Error
131131
~
@@ -136,7 +136,7 @@ tests/cases/conformance/types/conditional/inferTypes1.ts(153,40): error TS2322:
136136
!!! error TS2304: Cannot find name 'U'.
137137
~
138138
!!! error TS4081: Exported type alias 'T62' has or is using private name 'U'.
139-
type T63<T> = T extends (infer A extends infer B ? infer C : infer D) ? string : number;
139+
type T63<T> = T extends ((infer A) extends infer B ? infer C : infer D) ? string : number;
140140

141141
type T70<T extends string> = { x: T };
142142
type T71<T> = T extends T70<infer U> ? T70<U> : never;

tests/baselines/reference/inferTypes1.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ type T53 = X3<{ a: (x: number) => void, b: (x: string) => void }>; // never
8181
type T54 = X3<{ a: (x: number) => void, b: () => void }>; // number
8282

8383
type T60 = infer U; // Error
84-
type T61<T> = infer A extends infer B ? infer C : infer D; // Error
84+
type T61<T> = (infer A) extends infer B ? infer C : infer D; // Error
8585
type T62<T> = U extends (infer U)[] ? U : U; // Error
86-
type T63<T> = T extends (infer A extends infer B ? infer C : infer D) ? string : number;
86+
type T63<T> = T extends ((infer A) extends infer B ? infer C : infer D) ? string : number;
8787

8888
type T70<T extends string> = { x: T };
8989
type T71<T> = T extends T70<infer U> ? T70<U> : never;

tests/baselines/reference/inferTypes1.symbols

+16-16
Original file line numberDiff line numberDiff line change
@@ -349,33 +349,33 @@ type T60 = infer U; // Error
349349
>T60 : Symbol(T60, Decl(inferTypes1.ts, 79, 57))
350350
>U : Symbol(U, Decl(inferTypes1.ts, 81, 16))
351351

352-
type T61<T> = infer A extends infer B ? infer C : infer D; // Error
352+
type T61<T> = (infer A) extends infer B ? infer C : infer D; // Error
353353
>T61 : Symbol(T61, Decl(inferTypes1.ts, 81, 19))
354354
>T : Symbol(T, Decl(inferTypes1.ts, 82, 9))
355-
>A : Symbol(A, Decl(inferTypes1.ts, 82, 19))
356-
>B : Symbol(B, Decl(inferTypes1.ts, 82, 35))
357-
>C : Symbol(C, Decl(inferTypes1.ts, 82, 45))
358-
>D : Symbol(D, Decl(inferTypes1.ts, 82, 55))
355+
>A : Symbol(A, Decl(inferTypes1.ts, 82, 20))
356+
>B : Symbol(B, Decl(inferTypes1.ts, 82, 37))
357+
>C : Symbol(C, Decl(inferTypes1.ts, 82, 47))
358+
>D : Symbol(D, Decl(inferTypes1.ts, 82, 57))
359359

360360
type T62<T> = U extends (infer U)[] ? U : U; // Error
361-
>T62 : Symbol(T62, Decl(inferTypes1.ts, 82, 58))
361+
>T62 : Symbol(T62, Decl(inferTypes1.ts, 82, 60))
362362
>T : Symbol(T, Decl(inferTypes1.ts, 83, 9))
363363
>U : Symbol(U)
364364
>U : Symbol(U, Decl(inferTypes1.ts, 83, 30))
365365
>U : Symbol(U, Decl(inferTypes1.ts, 83, 30))
366366
>U : Symbol(U)
367367

368-
type T63<T> = T extends (infer A extends infer B ? infer C : infer D) ? string : number;
368+
type T63<T> = T extends ((infer A) extends infer B ? infer C : infer D) ? string : number;
369369
>T63 : Symbol(T63, Decl(inferTypes1.ts, 83, 44))
370370
>T : Symbol(T, Decl(inferTypes1.ts, 84, 9))
371371
>T : Symbol(T, Decl(inferTypes1.ts, 84, 9))
372-
>A : Symbol(A, Decl(inferTypes1.ts, 84, 30))
373-
>B : Symbol(B, Decl(inferTypes1.ts, 84, 46))
374-
>C : Symbol(C, Decl(inferTypes1.ts, 84, 56))
375-
>D : Symbol(D, Decl(inferTypes1.ts, 84, 66))
372+
>A : Symbol(A, Decl(inferTypes1.ts, 84, 31))
373+
>B : Symbol(B, Decl(inferTypes1.ts, 84, 48))
374+
>C : Symbol(C, Decl(inferTypes1.ts, 84, 58))
375+
>D : Symbol(D, Decl(inferTypes1.ts, 84, 68))
376376

377377
type T70<T extends string> = { x: T };
378-
>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 88))
378+
>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 90))
379379
>T : Symbol(T, Decl(inferTypes1.ts, 86, 9))
380380
>x : Symbol(x, Decl(inferTypes1.ts, 86, 30))
381381
>T : Symbol(T, Decl(inferTypes1.ts, 86, 9))
@@ -384,9 +384,9 @@ type T71<T> = T extends T70<infer U> ? T70<U> : never;
384384
>T71 : Symbol(T71, Decl(inferTypes1.ts, 86, 38))
385385
>T : Symbol(T, Decl(inferTypes1.ts, 87, 9))
386386
>T : Symbol(T, Decl(inferTypes1.ts, 87, 9))
387-
>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 88))
387+
>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 90))
388388
>U : Symbol(U, Decl(inferTypes1.ts, 87, 33))
389-
>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 88))
389+
>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 90))
390390
>U : Symbol(U, Decl(inferTypes1.ts, 87, 33))
391391

392392
type T72<T extends number> = { y: T };
@@ -401,7 +401,7 @@ type T73<T> = T extends T72<infer U> ? T70<U> : never; // Error
401401
>T : Symbol(T, Decl(inferTypes1.ts, 90, 9))
402402
>T72 : Symbol(T72, Decl(inferTypes1.ts, 87, 54))
403403
>U : Symbol(U, Decl(inferTypes1.ts, 90, 33))
404-
>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 88))
404+
>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 90))
405405
>U : Symbol(U, Decl(inferTypes1.ts, 90, 33))
406406

407407
type T74<T extends number, U extends string> = { x: T, y: U };
@@ -420,7 +420,7 @@ type T75<T> = T extends T74<infer U, infer U> ? T70<U> | T72<U> | T74<U, U> : ne
420420
>T74 : Symbol(T74, Decl(inferTypes1.ts, 90, 54))
421421
>U : Symbol(U, Decl(inferTypes1.ts, 93, 33), Decl(inferTypes1.ts, 93, 42))
422422
>U : Symbol(U, Decl(inferTypes1.ts, 93, 33), Decl(inferTypes1.ts, 93, 42))
423-
>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 88))
423+
>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 90))
424424
>U : Symbol(U, Decl(inferTypes1.ts, 93, 33), Decl(inferTypes1.ts, 93, 42))
425425
>T72 : Symbol(T72, Decl(inferTypes1.ts, 87, 54))
426426
>U : Symbol(U, Decl(inferTypes1.ts, 93, 33), Decl(inferTypes1.ts, 93, 42))

tests/baselines/reference/inferTypes1.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,13 @@ type T54 = X3<{ a: (x: number) => void, b: () => void }>; // number
253253
type T60 = infer U; // Error
254254
>T60 : U
255255

256-
type T61<T> = infer A extends infer B ? infer C : infer D; // Error
256+
type T61<T> = (infer A) extends infer B ? infer C : infer D; // Error
257257
>T61 : T61<T>
258258

259259
type T62<T> = U extends (infer U)[] ? U : U; // Error
260260
>T62 : any
261261

262-
type T63<T> = T extends (infer A extends infer B ? infer C : infer D) ? string : number;
262+
type T63<T> = T extends ((infer A) extends infer B ? infer C : infer D) ? string : number;
263263
>T63 : T63<T>
264264

265265
type T70<T extends string> = { x: T };

tests/baselines/reference/inferTypes2.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,5 @@ export declare function foo2<T>(obj: T): T extends {
4949
[K in keyof BadNested<infer P>]: BadNested<infer P>[K];
5050
} ? P : never;
5151
export declare function bar2<T>(obj: T): T extends {
52-
x: infer P extends number ? infer P : string;
52+
x: (infer P) extends number ? infer P : string;
5353
} ? P : never;

tests/baselines/reference/inferTypes2.types

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ export type BadNested<T> = { x: T extends number ? T : string };
2020
>x : T extends number ? T : string
2121

2222
export declare function foo2<T>(obj: T): T extends { [K in keyof BadNested<infer P>]: BadNested<infer P>[K] } ? P : never;
23-
>foo2 : <T>(obj: T) => T extends { x: infer P extends number ? infer P : string; } ? P : never
23+
>foo2 : <T>(obj: T) => T extends { x: (infer P) extends number ? infer P : string; } ? P : never
2424
>obj : T
2525

2626
export function bar2<T>(obj: T) {
27-
>bar2 : <T>(obj: T) => T extends { x: infer P extends number ? infer P : string; } ? P : never
27+
>bar2 : <T>(obj: T) => T extends { x: (infer P) extends number ? infer P : string; } ? P : never
2828
>obj : T
2929

3030
return foo2(obj);
31-
>foo2(obj) : T extends { x: infer P extends number ? infer P : string; } ? P : never
32-
>foo2 : <T>(obj: T) => T extends { x: infer P extends number ? infer P : string; } ? P : never
31+
>foo2(obj) : T extends { x: (infer P) extends number ? infer P : string; } ? P : never
32+
>foo2 : <T>(obj: T) => T extends { x: (infer P) extends number ? infer P : string; } ? P : never
3333
>obj : T
3434
}
3535

0 commit comments

Comments
 (0)