Skip to content

Commit 01b9a2d

Browse files
committed
Emit extends clause for synthetic infer typesduring declaration emit
1 parent 0aded71 commit 01b9a2d

File tree

6 files changed

+104
-6
lines changed

6 files changed

+104
-6
lines changed

src/compiler/checker.ts

+16-3
Original file line numberDiff line numberDiff line change
@@ -4977,7 +4977,20 @@ namespace ts {
49774977
if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) {
49784978
if (type.flags & TypeFlags.TypeParameter && contains(context.inferTypeParameters, type)) {
49794979
context.approximateLength += (symbolName(type.symbol).length + 6);
4980-
return factory.createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, /*constraintNode*/ undefined));
4980+
let constraintNode: TypeNode | undefined;
4981+
const constraint = getConstraintOfTypeParameter(type as TypeParameter);
4982+
if (constraint) {
4983+
// If the infer type has a constraint that is not the same as the constraint
4984+
// we would have normally inferred based on context, we emit the constraint
4985+
// using `infer T extends ?`. We omit inferred constraints from type references
4986+
// as they may be elided.
4987+
const inferredConstraint = getInferredTypeParameterConstraint(type as TypeParameter, /*omitTypeReferences*/ true);
4988+
if (!(inferredConstraint && isTypeIdenticalTo(constraint, inferredConstraint))) {
4989+
context.approximateLength += 9;
4990+
constraintNode = constraint && typeToTypeNodeHelper(constraint, context);
4991+
}
4992+
}
4993+
return factory.createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, constraintNode));
49814994
}
49824995
if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams &&
49834996
type.flags & TypeFlags.TypeParameter &&
@@ -13259,7 +13272,7 @@ namespace ts {
1325913272
return mapDefined(filter(type.symbol && type.symbol.declarations, isTypeParameterDeclaration), getEffectiveConstraintOfTypeParameter)[0];
1326013273
}
1326113274

13262-
function getInferredTypeParameterConstraint(typeParameter: TypeParameter) {
13275+
function getInferredTypeParameterConstraint(typeParameter: TypeParameter, omitTypeReferences?: boolean) {
1326313276
let inferences: Type[] | undefined;
1326413277
if (typeParameter.symbol?.declarations) {
1326513278
for (const declaration of typeParameter.symbol.declarations) {
@@ -13269,7 +13282,7 @@ namespace ts {
1326913282
// corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are
1327013283
// present, we form an intersection of the inferred constraint types.
1327113284
const [childTypeParameter = declaration.parent, grandParent] = walkUpParenthesizedTypesAndGetParentAndChild(declaration.parent.parent);
13272-
if (grandParent.kind === SyntaxKind.TypeReference) {
13285+
if (grandParent.kind === SyntaxKind.TypeReference && !omitTypeReferences) {
1327313286
const typeReference = grandParent as TypeReferenceNode;
1327413287
const typeParameters = getTypeParametersForTypeReference(typeReference);
1327513288
if (typeParameters) {

tests/baselines/reference/inferTypesWithExtends1.js

+22-1
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,26 @@ type X21_T5 = X21<1 | 2, 3>; // never
123123

124124
// from mongoose
125125
type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B;
126-
126+
127+
declare const x1: <T>() => (T extends infer U extends number ? 1 : 0);
128+
function f1() {
129+
return x1;
130+
}
131+
132+
type ExpectNumber<T extends number> = T;
133+
declare const x2: <T>() => (T extends ExpectNumber<infer U> ? 1 : 0);
134+
function f2() {
135+
return x2;
136+
}
127137

128138
//// [inferTypesWithExtends1.js]
129139
"use strict";
140+
function f1() {
141+
return x1;
142+
}
143+
function f2() {
144+
return x2;
145+
}
130146

131147

132148
//// [inferTypesWithExtends1.d.ts]
@@ -275,3 +291,8 @@ declare type X21_T3 = X21<1 | 2, 1>;
275291
declare type X21_T4 = X21<1 | 2, 2 | 3>;
276292
declare type X21_T5 = X21<1 | 2, 3>;
277293
declare type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B;
294+
declare const x1: <T>() => (T extends infer U extends number ? 1 : 0);
295+
declare function f1(): <T>() => T extends infer U extends number ? 1 : 0;
296+
declare type ExpectNumber<T extends number> = T;
297+
declare const x2: <T>() => (T extends ExpectNumber<infer U> ? 1 : 0);
298+
declare function f2(): <T>() => T extends infer U extends number ? 1 : 0;

tests/baselines/reference/inferTypesWithExtends1.symbols

+31
Original file line numberDiff line numberDiff line change
@@ -498,3 +498,34 @@ type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends <T>() => T ex
498498
>A : Symbol(A, Decl(inferTypesWithExtends1.ts, 123, 19))
499499
>B : Symbol(B, Decl(inferTypesWithExtends1.ts, 123, 22))
500500

501+
declare const x1: <T>() => (T extends infer U extends number ? 1 : 0);
502+
>x1 : Symbol(x1, Decl(inferTypesWithExtends1.ts, 125, 13))
503+
>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 125, 19))
504+
>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 125, 19))
505+
>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 125, 43))
506+
507+
function f1() {
508+
>f1 : Symbol(f1, Decl(inferTypesWithExtends1.ts, 125, 70))
509+
510+
return x1;
511+
>x1 : Symbol(x1, Decl(inferTypesWithExtends1.ts, 125, 13))
512+
}
513+
514+
type ExpectNumber<T extends number> = T;
515+
>ExpectNumber : Symbol(ExpectNumber, Decl(inferTypesWithExtends1.ts, 128, 1))
516+
>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 130, 18))
517+
>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 130, 18))
518+
519+
declare const x2: <T>() => (T extends ExpectNumber<infer U> ? 1 : 0);
520+
>x2 : Symbol(x2, Decl(inferTypesWithExtends1.ts, 131, 13))
521+
>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 131, 19))
522+
>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 131, 19))
523+
>ExpectNumber : Symbol(ExpectNumber, Decl(inferTypesWithExtends1.ts, 128, 1))
524+
>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 131, 56))
525+
526+
function f2() {
527+
>f2 : Symbol(f2, Decl(inferTypesWithExtends1.ts, 131, 69))
528+
529+
return x2;
530+
>x2 : Symbol(x2, Decl(inferTypesWithExtends1.ts, 131, 13))
531+
}

tests/baselines/reference/inferTypesWithExtends1.types

+22
Original file line numberDiff line numberDiff line change
@@ -307,3 +307,25 @@ type X21_T5 = X21<1 | 2, 3>; // never
307307
type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B;
308308
>IfEquals : IfEquals<X, Y, A, B>
309309

310+
declare const x1: <T>() => (T extends infer U extends number ? 1 : 0);
311+
>x1 : <T>() => T extends infer U extends number ? 1 : 0
312+
313+
function f1() {
314+
>f1 : () => <T>() => T extends infer U extends number ? 1 : 0
315+
316+
return x1;
317+
>x1 : <T>() => T extends infer U extends number ? 1 : 0
318+
}
319+
320+
type ExpectNumber<T extends number> = T;
321+
>ExpectNumber : T
322+
323+
declare const x2: <T>() => (T extends ExpectNumber<infer U> ? 1 : 0);
324+
>x2 : <T>() => T extends infer U extends number ? 1 : 0
325+
326+
function f2() {
327+
>f2 : () => <T>() => T extends infer U extends number ? 1 : 0
328+
329+
return x2;
330+
>x2 : <T>() => T extends infer U extends number ? 1 : 0
331+
}

tests/baselines/reference/infiniteConstraints.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type AProp<T extends { a: string }> = T
1515
>a : string
1616

1717
declare function myBug<
18-
>myBug : <T extends { [K in keyof T]: T[K] extends infer U ? U : never; }>(arg: T) => T
18+
>myBug : <T extends { [K in keyof T]: T[K] extends infer U extends { a: string; } ? U : never; }>(arg: T) => T
1919

2020
T extends { [K in keyof T]: T[K] extends AProp<infer U> ? U : never }
2121
>(arg: T): T
@@ -24,7 +24,7 @@ declare function myBug<
2424
const out = myBug({obj1: {a: "test"}})
2525
>out : { obj1: { a: string; }; }
2626
>myBug({obj1: {a: "test"}}) : { obj1: { a: string; }; }
27-
>myBug : <T extends { [K in keyof T]: T[K] extends infer U ? U : never; }>(arg: T) => T
27+
>myBug : <T extends { [K in keyof T]: T[K] extends infer U extends { a: string; } ? U : never; }>(arg: T) => T
2828
>{obj1: {a: "test"}} : { obj1: { a: string; }; }
2929
>obj1 : { a: string; }
3030
>{a: "test"} : { a: string; }

tests/cases/conformance/types/conditional/inferTypesWithExtends1.ts

+11
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,14 @@ type X21_T5 = X21<1 | 2, 3>; // never
125125

126126
// from mongoose
127127
type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B;
128+
129+
declare const x1: <T>() => (T extends infer U extends number ? 1 : 0);
130+
function f1() {
131+
return x1;
132+
}
133+
134+
type ExpectNumber<T extends number> = T;
135+
declare const x2: <T>() => (T extends ExpectNumber<infer U> ? 1 : 0);
136+
function f2() {
137+
return x2;
138+
}

0 commit comments

Comments
 (0)