Skip to content

Commit 9dd3148

Browse files
authored
Merge pull request microsoft#22959 from Microsoft/fixInferTypeParameterLeak
Fix 'infer T' type parameter leaks
2 parents 5472ca9 + 6fedf4d commit 9dd3148

File tree

7 files changed

+398
-296
lines changed

7 files changed

+398
-296
lines changed

src/compiler/checker.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6233,7 +6233,7 @@ namespace ts {
62336233
}
62346234

62356235
function getDefaultConstraintOfConditionalType(type: ConditionalType) {
6236-
return getUnionType([getTrueTypeFromConditionalType(type), getFalseTypeFromConditionalType(type)]);
6236+
return getUnionType([getInferredTrueTypeFromConditionalType(type), getFalseTypeFromConditionalType(type)]);
62376237
}
62386238

62396239
function getConstraintOfDistributiveConditionalType(type: ConditionalType): Type {
@@ -8343,16 +8343,19 @@ namespace ts {
83438343
// If this is a distributive conditional type and the check type is generic we need to defer
83448344
// resolution of the conditional type such that a later instantiation will properly distribute
83458345
// over union types.
8346-
if (!root.isDistributive || !maybeTypeOfKind(checkType, TypeFlags.Instantiable)) {
8346+
const isDeferred = root.isDistributive && maybeTypeOfKind(checkType, TypeFlags.Instantiable);
83478347
let combinedMapper: TypeMapper;
83488348
if (root.inferTypeParameters) {
83498349
const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None);
8350+
if (!isDeferred) {
83508351
// We don't want inferences from constraints as they may cause us to eagerly resolve the
83518352
// conditional type instead of deferring resolution. Also, we always want strict function
83528353
// types rules (i.e. proper contravariance) for inferences.
83538354
inferTypes(context.inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
8355+
}
83548356
combinedMapper = combineTypeMappers(mapper, context);
83558357
}
8358+
if (!isDeferred) {
83568359
// Return union of trueType and falseType for 'any' since it matches anything
83578360
if (checkType.flags & TypeFlags.Any) {
83588361
return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), instantiateType(root.falseType, mapper)]);
@@ -8381,6 +8384,7 @@ namespace ts {
83818384
result.checkType = erasedCheckType;
83828385
result.extendsType = extendsType;
83838386
result.mapper = mapper;
8387+
result.combinedMapper = combinedMapper;
83848388
result.aliasSymbol = root.aliasSymbol;
83858389
result.aliasTypeArguments = instantiateTypes(root.aliasTypeArguments, mapper);
83868390
return result;
@@ -8394,6 +8398,12 @@ namespace ts {
83948398
return type.resolvedFalseType || (type.resolvedFalseType = instantiateType(type.root.falseType, type.mapper));
83958399
}
83968400

8401+
function getInferredTrueTypeFromConditionalType(type: ConditionalType) {
8402+
return type.combinedMapper ?
8403+
type.resolvedInferredTrueType || (type.resolvedInferredTrueType = instantiateType(type.root.trueType, type.combinedMapper)) :
8404+
getTrueTypeFromConditionalType(type);
8405+
}
8406+
83978407
function getInferTypeParameters(node: ConditionalTypeNode): TypeParameter[] {
83988408
let result: TypeParameter[];
83998409
if (node.locals) {

src/compiler/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3878,7 +3878,11 @@ namespace ts {
38783878
resolvedTrueType?: Type;
38793879
resolvedFalseType?: Type;
38803880
/* @internal */
3881+
resolvedInferredTrueType?: Type;
3882+
/* @internal */
38813883
mapper?: TypeMapper;
3884+
/* @internal */
3885+
combinedMapper?: TypeMapper;
38823886
}
38833887

38843888
// Type parameter substitution (TypeFlags.Substitution)

tests/baselines/reference/conditionalTypes1.errors.txt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(159,5): error TS2
6464
tests/cases/conformance/types/conditional/conditionalTypes1.ts(160,5): error TS2322: Type 'T' is not assignable to type 'ZeroOf<T>'.
6565
Type 'string | number' is not assignable to type 'ZeroOf<T>'.
6666
Type 'string' is not assignable to type 'ZeroOf<T>'.
67-
tests/cases/conformance/types/conditional/conditionalTypes1.ts(255,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'z' must be of type 'T1', but here has type 'Foo<T & U>'.
68-
tests/cases/conformance/types/conditional/conditionalTypes1.ts(280,43): error TS2322: Type 'T95<U>' is not assignable to type 'T94<U>'.
67+
tests/cases/conformance/types/conditional/conditionalTypes1.ts(263,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'z' must be of type 'T1', but here has type 'Foo<T & U>'.
68+
tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS2322: Type 'T95<U>' is not assignable to type 'T94<U>'.
6969
Type 'boolean' is not assignable to type 'true'.
7070

7171

@@ -370,6 +370,14 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(280,43): error TS
370370
type T51 = IsNever<number>; // false
371371
type T52 = IsNever<any>; // false
372372

373+
function f22<T>(x: T extends (infer U)[] ? U[] : never) {
374+
let e = x[0]; // {}
375+
}
376+
377+
function f23<T extends string[]>(x: T extends (infer U)[] ? U[] : never) {
378+
let e = x[0]; // string
379+
}
380+
373381
// Repros from #21664
374382

375383
type Eq<T, U> = T extends U ? U extends T ? true : false : false;

tests/baselines/reference/conditionalTypes1.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,14 @@ type T50 = IsNever<never>; // true
213213
type T51 = IsNever<number>; // false
214214
type T52 = IsNever<any>; // false
215215

216+
function f22<T>(x: T extends (infer U)[] ? U[] : never) {
217+
let e = x[0]; // {}
218+
}
219+
220+
function f23<T extends string[]>(x: T extends (infer U)[] ? U[] : never) {
221+
let e = x[0]; // string
222+
}
223+
216224
// Repros from #21664
217225

218226
type Eq<T, U> = T extends U ? U extends T ? true : false : false;
@@ -398,6 +406,12 @@ function f21(x, y) {
398406
x = y; // Error
399407
y = x; // Error
400408
}
409+
function f22(x) {
410+
var e = x[0]; // {}
411+
}
412+
function f23(x) {
413+
var e = x[0]; // string
414+
}
401415
var convert = function (value) { return value; };
402416
var convert2 = function (value) { return value; };
403417
function f31() {
@@ -590,6 +604,8 @@ declare type IsNever<T> = [T] extends [never] ? true : false;
590604
declare type T50 = IsNever<never>;
591605
declare type T51 = IsNever<number>;
592606
declare type T52 = IsNever<any>;
607+
declare function f22<T>(x: T extends (infer U)[] ? U[] : never): void;
608+
declare function f23<T extends string[]>(x: T extends (infer U)[] ? U[] : never): void;
593609
declare type Eq<T, U> = T extends U ? U extends T ? true : false : false;
594610
declare type T60 = Eq<true, true>;
595611
declare type T61 = Eq<true, false>;

0 commit comments

Comments
 (0)