Skip to content

Improve reduction of similar intersections in type inference #51405

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 7 additions & 0 deletions tests/baselines/reference/unionAndIntersectionInference3.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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 {};
16 changes: 16 additions & 0 deletions tests/baselines/reference/unionAndIntersectionInference3.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -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))

19 changes: 19 additions & 0 deletions tests/baselines/reference/unionAndIntersectionInference3.types
Original file line number Diff line number Diff line change
Expand Up @@ -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

Original file line number Diff line number Diff line change
Expand Up @@ -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;