diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 685fc7a9ee149..1bacbfbead2e4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22728,14 +22728,11 @@ namespace ts { } source = getUnionType(sources); } - else if (target.flags & TypeFlags.Intersection && some((target as IntersectionType).types, - t => !!getInferenceInfoForType(t) || (isGenericMappedType(t) && !!getInferenceInfoForType(getHomomorphicTypeVariable(t) || neverType)))) { - // We reduce intersection types only when they contain naked type parameters. For example, when - // inferring from 'string[] & { extra: any }' to 'string[] & T' we want to remove string[] and + else if (target.flags & TypeFlags.Intersection && !every((target as IntersectionType).types, isNonGenericObjectType)) { + // We reduce intersection types unless they're simple combinations of object types. For example, + // when inferring from 'string[] & { extra: any }' to 'string[] & T' we want to remove string[] and // infer { extra: any } for T. But when inferring to 'string[] & Iterable' we want to keep the // string[] on the source side and infer string for T. - // Likewise, we consider a homomorphic mapped type constrainted to the target type parameter as similar to a "naked type variable" - // in such scenarios. if (!(source.flags & TypeFlags.Union)) { // Infer between identically matching source and target constituents and remove the matching types. const [sources, targets] = inferFromMatchingTypes(source.flags & TypeFlags.Intersection ? (source as IntersectionType).types : [source], (target as IntersectionType).types, isTypeIdenticalTo); diff --git a/tests/baselines/reference/unionAndIntersectionInference3.js b/tests/baselines/reference/unionAndIntersectionInference3.js index c39914f090209..bdebfb307bd28 100644 --- a/tests/baselines/reference/unionAndIntersectionInference3.js +++ b/tests/baselines/reference/unionAndIntersectionInference3.js @@ -86,6 +86,12 @@ declare function foo(obj: T & AB): [T, U]; declare let ab: AB; let z = foo(ab); // [AB, string] + +// Repro from #51399 + +declare let a: () => (T extends true ? true : false) & boolean; +declare let b: () => (T extends true ? true : false) & boolean; +a = b; //// [unionAndIntersectionInference3.js] @@ -105,4 +111,5 @@ let x2 = foo2(sa); // unknown let y2 = foo2(sx); // { extra: number } withRouter(MyComponent); let z = foo(ab); // [AB, string] +a = b; export {}; diff --git a/tests/baselines/reference/unionAndIntersectionInference3.symbols b/tests/baselines/reference/unionAndIntersectionInference3.symbols index 43079f9e8a958..3319b26780433 100644 --- a/tests/baselines/reference/unionAndIntersectionInference3.symbols +++ b/tests/baselines/reference/unionAndIntersectionInference3.symbols @@ -309,3 +309,19 @@ let z = foo(ab); // [AB, string] >foo : Symbol(foo, Decl(unionAndIntersectionInference3.ts, 80, 33)) >ab : Symbol(ab, Decl(unionAndIntersectionInference3.ts, 84, 11)) +// Repro from #51399 + +declare let a: () => (T extends true ? true : false) & boolean; +>a : Symbol(a, Decl(unionAndIntersectionInference3.ts, 90, 11)) +>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 90, 16)) +>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 90, 16)) + +declare let b: () => (T extends true ? true : false) & boolean; +>b : Symbol(b, Decl(unionAndIntersectionInference3.ts, 91, 11)) +>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 91, 16)) +>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 91, 16)) + +a = b; +>a : Symbol(a, Decl(unionAndIntersectionInference3.ts, 90, 11)) +>b : Symbol(b, Decl(unionAndIntersectionInference3.ts, 91, 11)) + diff --git a/tests/baselines/reference/unionAndIntersectionInference3.types b/tests/baselines/reference/unionAndIntersectionInference3.types index 5990df7451662..7084e21994788 100644 --- a/tests/baselines/reference/unionAndIntersectionInference3.types +++ b/tests/baselines/reference/unionAndIntersectionInference3.types @@ -199,3 +199,22 @@ let z = foo(ab); // [AB, string] >foo : (obj: T & AB) => [T, U] >ab : AB +// Repro from #51399 + +declare let a: () => (T extends true ? true : false) & boolean; +>a : () => (T extends true ? true : false) & boolean +>true : true +>true : true +>false : false + +declare let b: () => (T extends true ? true : false) & boolean; +>b : () => (T extends true ? true : false) & boolean +>true : true +>true : true +>false : false + +a = b; +>a = b : () => (T extends true ? true : false) & boolean +>a : () => (T extends true ? true : false) & boolean +>b : () => (T extends true ? true : false) & boolean + diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts b/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts index 12a5e8b17cb1f..b4957c78b81c1 100644 --- a/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts +++ b/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts @@ -88,3 +88,9 @@ declare function foo(obj: T & AB): [T, U]; declare let ab: AB; let z = foo(ab); // [AB, string] + +// Repro from #51399 + +declare let a: () => (T extends true ? true : false) & boolean; +declare let b: () => (T extends true ? true : false) & boolean; +a = b;