Skip to content

Commit bafe193

Browse files
committed
Add 'extends' clause to 'infer' type
1 parent 0a24dee commit bafe193

23 files changed

+1017
-55
lines changed

src/compiler/checker.ts

+19-3
Original file line numberDiff line numberDiff line change
@@ -35335,6 +35335,22 @@ namespace ts {
3533535335
grammarErrorOnNode(node, Diagnostics.infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type);
3533635336
}
3533735337
checkSourceElement(node.typeParameter);
35338+
const symbol = getSymbolOfNode(node.typeParameter);
35339+
if (symbol.declarations && symbol.declarations.length > 1) {
35340+
const links = getSymbolLinks(symbol);
35341+
if (!links.typeParametersChecked) {
35342+
links.typeParametersChecked = true;
35343+
const typeParameter = getDeclaredTypeOfTypeParameter(symbol);
35344+
const declarations: TypeParameterDeclaration[] = getDeclarationsOfKind(symbol, SyntaxKind.TypeParameter);
35345+
if (!areTypeParametersIdentical(declarations, [typeParameter], decl => [decl])) {
35346+
// Report an error on every conflicting declaration.
35347+
const name = symbolToString(symbol);
35348+
for (const declaration of declarations) {
35349+
error(declaration.name, Diagnostics.All_declarations_of_0_must_have_identical_constraints, name);
35350+
}
35351+
}
35352+
}
35353+
}
3533835354
registerForUnusedIdentifiersCheck(node);
3533935355
}
3534035356

@@ -38811,7 +38827,7 @@ namespace ts {
3881138827
}
3881238828

3881338829
const type = getDeclaredTypeOfSymbol(symbol) as InterfaceType;
38814-
if (!areTypeParametersIdentical(declarations, type.localTypeParameters!)) {
38830+
if (!areTypeParametersIdentical(declarations, type.localTypeParameters!, getEffectiveTypeParameterDeclarations)) {
3881538831
// Report an error on every conflicting declaration.
3881638832
const name = symbolToString(symbol);
3881738833
for (const declaration of declarations) {
@@ -38821,13 +38837,13 @@ namespace ts {
3882138837
}
3882238838
}
3882338839

38824-
function areTypeParametersIdentical(declarations: readonly (ClassDeclaration | InterfaceDeclaration)[], targetParameters: TypeParameter[]) {
38840+
function areTypeParametersIdentical<T extends DeclarationWithTypeParameters | TypeParameterDeclaration>(declarations: readonly T[], targetParameters: TypeParameter[], getTypeParameterDeclarations: (node: T) => readonly TypeParameterDeclaration[]) {
3882538841
const maxTypeArgumentCount = length(targetParameters);
3882638842
const minTypeArgumentCount = getMinTypeArgumentCount(targetParameters);
3882738843

3882838844
for (const declaration of declarations) {
3882938845
// If this declaration has too few or too many type parameters, we report an error
38830-
const sourceParameters = getEffectiveTypeParameterDeclarations(declaration);
38846+
const sourceParameters = getTypeParameterDeclarations(declaration);
3883138847
const numTypeParameters = sourceParameters.length;
3883238848
if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) {
3883338849
return false;

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -3405,6 +3405,10 @@
34053405
"category": "Error",
34063406
"code": 2837
34073407
},
3408+
"All declarations of '{0}' must have identical constraints.": {
3409+
"category": "Error",
3410+
"code": 2838
3411+
},
34083412

34093413
"Import declaration '{0}' is using private name '{1}'.": {
34103414
"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
@@ -3077,6 +3077,10 @@ namespace ts {
30773077
}
30783078

30793079
function parseTypeParameter(): TypeParameterDeclaration {
3080+
return parseTypeParameterWorker(/*canHaveDefault*/ true);
3081+
}
3082+
3083+
function parseTypeParameterWorker(canHaveDefault: boolean): TypeParameterDeclaration {
30803084
const pos = getNodePos();
30813085
const name = parseIdentifier();
30823086
let constraint: TypeNode | undefined;
@@ -3101,7 +3105,7 @@ namespace ts {
31013105
}
31023106
}
31033107

3104-
const defaultType = parseOptional(SyntaxKind.EqualsToken) ? parseType() : undefined;
3108+
const defaultType = canHaveDefault && parseOptional(SyntaxKind.EqualsToken) ? parseType() : undefined;
31053109
const node = factory.createTypeParameterDeclaration(name, constraint, defaultType);
31063110
node.expression = expression;
31073111
return finishNode(node, pos);
@@ -3859,22 +3863,10 @@ namespace ts {
38593863
return finishNode(factory.createTypeOperatorNode(operator, parseTypeOperatorOrHigher()), pos);
38603864
}
38613865

3862-
function parseTypeParameterOfInferType() {
3863-
const pos = getNodePos();
3864-
return finishNode(
3865-
factory.createTypeParameterDeclaration(
3866-
parseIdentifier(),
3867-
/*constraint*/ undefined,
3868-
/*defaultType*/ undefined
3869-
),
3870-
pos
3871-
);
3872-
}
3873-
38743866
function parseInferType(): InferTypeNode {
38753867
const pos = getNodePos();
38763868
parseExpected(SyntaxKind.InferKeyword);
3877-
return finishNode(factory.createInferTypeNode(parseTypeParameterOfInferType()), pos);
3869+
return finishNode(factory.createInferTypeNode(parseTypeParameterWorker(/*canHaveDefault*/ false)), pos);
38783870
}
38793871

38803872
function parseTypeOperatorOrHigher(): TypeNode {

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7090,6 +7090,7 @@ namespace ts {
70907090
parenthesizeExpressionOfExpressionStatement(expression: Expression): Expression;
70917091
parenthesizeConciseBodyOfArrowFunction(body: Expression): Expression;
70927092
parenthesizeConciseBodyOfArrowFunction(body: ConciseBody): ConciseBody;
7093+
parenthesizeCheckTypeOfConditionalType(member: TypeNode): TypeNode;
70937094
parenthesizeMemberOfConditionalType(member: TypeNode): TypeNode;
70947095
parenthesizeMemberOfElementType(member: TypeNode): TypeNode;
70957096
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)