Improve reduction of similar intersections in type inference (#51405)

* Change criteria for reducing intersections in type inference

* Add regression test
This commit is contained in:
Anders Hejlsberg 2022-11-04 17:04:05 -07:00 committed by GitHub
parent f1d62f4dc4
commit f0216e3421
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 51 additions and 6 deletions

View File

@ -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<T>' 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);

View File

@ -86,6 +86,12 @@ declare function foo<T, U>(obj: T & AB<U>): [T, U];
declare let ab: AB<string>;
let z = foo(ab); // [AB<string>, string]
// Repro from #51399
declare let a: <T>() => (T extends true ? true : false) & boolean;
declare let b: <T>() => (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>, string]
a = b;
export {};

View File

@ -309,3 +309,19 @@ let z = foo(ab); // [AB<string>, string]
>foo : Symbol(foo, Decl(unionAndIntersectionInference3.ts, 80, 33))
>ab : Symbol(ab, Decl(unionAndIntersectionInference3.ts, 84, 11))
// Repro from #51399
declare let a: <T>() => (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>() => (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))

View File

@ -199,3 +199,22 @@ let z = foo(ab); // [AB<string>, string]
>foo : <T, U>(obj: T & AB<U>) => [T, U]
>ab : AB<string>
// Repro from #51399
declare let a: <T>() => (T extends true ? true : false) & boolean;
>a : <T>() => (T extends true ? true : false) & boolean
>true : true
>true : true
>false : false
declare let b: <T>() => (T extends true ? true : false) & boolean;
>b : <T>() => (T extends true ? true : false) & boolean
>true : true
>true : true
>false : false
a = b;
>a = b : <T>() => (T extends true ? true : false) & boolean
>a : <T>() => (T extends true ? true : false) & boolean
>b : <T>() => (T extends true ? true : false) & boolean

View File

@ -88,3 +88,9 @@ declare function foo<T, U>(obj: T & AB<U>): [T, U];
declare let ab: AB<string>;
let z = foo(ab); // [AB<string>, string]
// Repro from #51399
declare let a: <T>() => (T extends true ? true : false) & boolean;
declare let b: <T>() => (T extends true ? true : false) & boolean;
a = b;