Have getAssignmentReducedType use the comparable relation instead of
typeMaybeAssignableTo. typeMaybeAssignableTo decomposed unions at the top level of the assigned type but didn't properly handle other unions that arose during assignability checking, e.g., in the constraint of a generic lookup type. Fixes #26130.
This commit is contained in:
parent
76f7ee998a
commit
d45e422b46
|
@ -13791,18 +13791,6 @@ namespace ts {
|
|||
return flow.id;
|
||||
}
|
||||
|
||||
function typeMaybeAssignableTo(source: Type, target: Type) {
|
||||
if (!(source.flags & TypeFlags.Union)) {
|
||||
return isTypeAssignableTo(source, target);
|
||||
}
|
||||
for (const t of (<UnionType>source).types) {
|
||||
if (isTypeAssignableTo(t, target)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove those constituent types of declaredType to which no constituent type of assignedType is assignable.
|
||||
// For example, when a variable of type number | string | boolean is assigned a value of type number | boolean,
|
||||
// we remove type string.
|
||||
|
@ -13811,7 +13799,7 @@ namespace ts {
|
|||
if (assignedType.flags & TypeFlags.Never) {
|
||||
return assignedType;
|
||||
}
|
||||
const reducedType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t));
|
||||
const reducedType = filterType(declaredType, t => isTypeComparableTo(assignedType, t));
|
||||
if (!(reducedType.flags & TypeFlags.Never)) {
|
||||
return reducedType;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
//// [assignmentGenericLookupTypeNarrowing.ts]
|
||||
// Repro from #26130
|
||||
|
||||
let mappedObject: {[K in "foo"]: null | {x: string}} = {foo: {x: "hello"}};
|
||||
declare function foo<T>(x: T): null | T;
|
||||
|
||||
function bar<K extends "foo">(key: K) {
|
||||
const element = foo(mappedObject[key]);
|
||||
if (element == null)
|
||||
return;
|
||||
const x = element.x;
|
||||
}
|
||||
|
||||
|
||||
//// [assignmentGenericLookupTypeNarrowing.js]
|
||||
// Repro from #26130
|
||||
var mappedObject = { foo: { x: "hello" } };
|
||||
function bar(key) {
|
||||
var element = foo(mappedObject[key]);
|
||||
if (element == null)
|
||||
return;
|
||||
var x = element.x;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
=== tests/cases/conformance/expressions/assignmentOperator/assignmentGenericLookupTypeNarrowing.ts ===
|
||||
// Repro from #26130
|
||||
|
||||
let mappedObject: {[K in "foo"]: null | {x: string}} = {foo: {x: "hello"}};
|
||||
>mappedObject : Symbol(mappedObject, Decl(assignmentGenericLookupTypeNarrowing.ts, 2, 3))
|
||||
>K : Symbol(K, Decl(assignmentGenericLookupTypeNarrowing.ts, 2, 20))
|
||||
>x : Symbol(x, Decl(assignmentGenericLookupTypeNarrowing.ts, 2, 41))
|
||||
>foo : Symbol(foo, Decl(assignmentGenericLookupTypeNarrowing.ts, 2, 56))
|
||||
>x : Symbol(x, Decl(assignmentGenericLookupTypeNarrowing.ts, 2, 62))
|
||||
|
||||
declare function foo<T>(x: T): null | T;
|
||||
>foo : Symbol(foo, Decl(assignmentGenericLookupTypeNarrowing.ts, 2, 75))
|
||||
>T : Symbol(T, Decl(assignmentGenericLookupTypeNarrowing.ts, 3, 21))
|
||||
>x : Symbol(x, Decl(assignmentGenericLookupTypeNarrowing.ts, 3, 24))
|
||||
>T : Symbol(T, Decl(assignmentGenericLookupTypeNarrowing.ts, 3, 21))
|
||||
>T : Symbol(T, Decl(assignmentGenericLookupTypeNarrowing.ts, 3, 21))
|
||||
|
||||
function bar<K extends "foo">(key: K) {
|
||||
>bar : Symbol(bar, Decl(assignmentGenericLookupTypeNarrowing.ts, 3, 40))
|
||||
>K : Symbol(K, Decl(assignmentGenericLookupTypeNarrowing.ts, 5, 13))
|
||||
>key : Symbol(key, Decl(assignmentGenericLookupTypeNarrowing.ts, 5, 30))
|
||||
>K : Symbol(K, Decl(assignmentGenericLookupTypeNarrowing.ts, 5, 13))
|
||||
|
||||
const element = foo(mappedObject[key]);
|
||||
>element : Symbol(element, Decl(assignmentGenericLookupTypeNarrowing.ts, 6, 7))
|
||||
>foo : Symbol(foo, Decl(assignmentGenericLookupTypeNarrowing.ts, 2, 75))
|
||||
>mappedObject : Symbol(mappedObject, Decl(assignmentGenericLookupTypeNarrowing.ts, 2, 3))
|
||||
>key : Symbol(key, Decl(assignmentGenericLookupTypeNarrowing.ts, 5, 30))
|
||||
|
||||
if (element == null)
|
||||
>element : Symbol(element, Decl(assignmentGenericLookupTypeNarrowing.ts, 6, 7))
|
||||
|
||||
return;
|
||||
const x = element.x;
|
||||
>x : Symbol(x, Decl(assignmentGenericLookupTypeNarrowing.ts, 9, 7))
|
||||
>element.x : Symbol(x, Decl(assignmentGenericLookupTypeNarrowing.ts, 2, 41))
|
||||
>element : Symbol(element, Decl(assignmentGenericLookupTypeNarrowing.ts, 6, 7))
|
||||
>x : Symbol(x, Decl(assignmentGenericLookupTypeNarrowing.ts, 2, 41))
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
=== tests/cases/conformance/expressions/assignmentOperator/assignmentGenericLookupTypeNarrowing.ts ===
|
||||
// Repro from #26130
|
||||
|
||||
let mappedObject: {[K in "foo"]: null | {x: string}} = {foo: {x: "hello"}};
|
||||
>mappedObject : { foo: { x: string; }; }
|
||||
>null : null
|
||||
>x : string
|
||||
>{foo: {x: "hello"}} : { foo: { x: string; }; }
|
||||
>foo : { x: string; }
|
||||
>{x: "hello"} : { x: string; }
|
||||
>x : string
|
||||
>"hello" : "hello"
|
||||
|
||||
declare function foo<T>(x: T): null | T;
|
||||
>foo : <T>(x: T) => T
|
||||
>x : T
|
||||
>null : null
|
||||
|
||||
function bar<K extends "foo">(key: K) {
|
||||
>bar : <K extends "foo">(key: K) => void
|
||||
>key : K
|
||||
|
||||
const element = foo(mappedObject[key]);
|
||||
>element : { foo: { x: string; }; }[K]
|
||||
>foo(mappedObject[key]) : { foo: { x: string; }; }[K]
|
||||
>foo : <T>(x: T) => T
|
||||
>mappedObject[key] : { foo: { x: string; }; }[K]
|
||||
>mappedObject : { foo: { x: string; }; }
|
||||
>key : K
|
||||
|
||||
if (element == null)
|
||||
>element == null : boolean
|
||||
>element : { foo: { x: string; }; }[K]
|
||||
>null : null
|
||||
|
||||
return;
|
||||
const x = element.x;
|
||||
>x : string
|
||||
>element.x : string
|
||||
>element : { foo: { x: string; }; }[K]
|
||||
>x : string
|
||||
}
|
||||
|
|
@ -252,9 +252,9 @@ abc = merged; // missing 'd'
|
|||
>merged : Merged.E
|
||||
|
||||
merged = abc; // ok
|
||||
>merged = abc : First.E
|
||||
>merged = abc : First.E.a | First.E.b
|
||||
>merged : Merged.E
|
||||
>abc : First.E
|
||||
>abc : First.E.a | First.E.b
|
||||
|
||||
abc = merged2; // ok
|
||||
>abc = merged2 : Merged2.E
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// Repro from #26130
|
||||
|
||||
let mappedObject: {[K in "foo"]: null | {x: string}} = {foo: {x: "hello"}};
|
||||
declare function foo<T>(x: T): null | T;
|
||||
|
||||
function bar<K extends "foo">(key: K) {
|
||||
const element = foo(mappedObject[key]);
|
||||
if (element == null)
|
||||
return;
|
||||
const x = element.x;
|
||||
}
|
Loading…
Reference in a new issue