Fix "Union types are not being narrowed down correctly in 4.3 #44401" (#44771)

* Fix "Union types are not being narrowed down correctly in 4.3 #44401"

* On undefined constraint, add the original type to constraints.
This commit is contained in:
caojoshua
2021-07-13 11:02:18 -07:00
committed by GitHub
parent c17fd8c479
commit acdf62fa1e
5 changed files with 134 additions and 1 deletions

View File

@@ -18363,7 +18363,7 @@ namespace ts {
// parameter 'T extends 1 | 2', the intersection 'T & 1' should be reduced to '1' such that it doesn't
// appear to be comparable to '2'.
if (relation === comparableRelation && target.flags & TypeFlags.Primitive) {
const constraints = sameMap((source as IntersectionType).types, t => t.flags & TypeFlags.Primitive ? t : getBaseConstraintOfType(t) || unknownType);
const constraints = sameMap((source as IntersectionType).types, getBaseConstraintOrType);
if (constraints !== (source as IntersectionType).types) {
source = getIntersectionType(constraints);
if (!(source.flags & TypeFlags.Intersection)) {

View File

@@ -0,0 +1,26 @@
//// [intersectionOfUnionNarrowing.ts]
interface X {
a?: { aProp: string };
b?: { bProp: string };
}
type AorB = { a: object; b: undefined } | { a: undefined; b: object };
declare const q: X & AorB;
if (q.a !== undefined) {
q.a.aProp;
} else {
// q.b is previously incorrectly inferred as potentially undefined
q.b.bProp;
}
//// [intersectionOfUnionNarrowing.js]
"use strict";
if (q.a !== undefined) {
q.a.aProp;
}
else {
// q.b is previously incorrectly inferred as potentially undefined
q.b.bProp;
}

View File

@@ -0,0 +1,47 @@
=== tests/cases/conformance/types/intersection/intersectionOfUnionNarrowing.ts ===
interface X {
>X : Symbol(X, Decl(intersectionOfUnionNarrowing.ts, 0, 0))
a?: { aProp: string };
>a : Symbol(X.a, Decl(intersectionOfUnionNarrowing.ts, 0, 13))
>aProp : Symbol(aProp, Decl(intersectionOfUnionNarrowing.ts, 1, 7))
b?: { bProp: string };
>b : Symbol(X.b, Decl(intersectionOfUnionNarrowing.ts, 1, 24))
>bProp : Symbol(bProp, Decl(intersectionOfUnionNarrowing.ts, 2, 7))
}
type AorB = { a: object; b: undefined } | { a: undefined; b: object };
>AorB : Symbol(AorB, Decl(intersectionOfUnionNarrowing.ts, 3, 1))
>a : Symbol(a, Decl(intersectionOfUnionNarrowing.ts, 4, 13))
>b : Symbol(b, Decl(intersectionOfUnionNarrowing.ts, 4, 24))
>a : Symbol(a, Decl(intersectionOfUnionNarrowing.ts, 4, 43))
>b : Symbol(b, Decl(intersectionOfUnionNarrowing.ts, 4, 57))
declare const q: X & AorB;
>q : Symbol(q, Decl(intersectionOfUnionNarrowing.ts, 6, 13))
>X : Symbol(X, Decl(intersectionOfUnionNarrowing.ts, 0, 0))
>AorB : Symbol(AorB, Decl(intersectionOfUnionNarrowing.ts, 3, 1))
if (q.a !== undefined) {
>q.a : Symbol(a, Decl(intersectionOfUnionNarrowing.ts, 0, 13), Decl(intersectionOfUnionNarrowing.ts, 4, 13), Decl(intersectionOfUnionNarrowing.ts, 0, 13), Decl(intersectionOfUnionNarrowing.ts, 4, 43))
>q : Symbol(q, Decl(intersectionOfUnionNarrowing.ts, 6, 13))
>a : Symbol(a, Decl(intersectionOfUnionNarrowing.ts, 0, 13), Decl(intersectionOfUnionNarrowing.ts, 4, 13), Decl(intersectionOfUnionNarrowing.ts, 0, 13), Decl(intersectionOfUnionNarrowing.ts, 4, 43))
>undefined : Symbol(undefined)
q.a.aProp;
>q.a.aProp : Symbol(aProp, Decl(intersectionOfUnionNarrowing.ts, 1, 7))
>q.a : Symbol(a, Decl(intersectionOfUnionNarrowing.ts, 0, 13), Decl(intersectionOfUnionNarrowing.ts, 4, 13))
>q : Symbol(q, Decl(intersectionOfUnionNarrowing.ts, 6, 13))
>a : Symbol(a, Decl(intersectionOfUnionNarrowing.ts, 0, 13), Decl(intersectionOfUnionNarrowing.ts, 4, 13))
>aProp : Symbol(aProp, Decl(intersectionOfUnionNarrowing.ts, 1, 7))
} else {
// q.b is previously incorrectly inferred as potentially undefined
q.b.bProp;
>q.b.bProp : Symbol(bProp, Decl(intersectionOfUnionNarrowing.ts, 2, 7))
>q.b : Symbol(b, Decl(intersectionOfUnionNarrowing.ts, 1, 24), Decl(intersectionOfUnionNarrowing.ts, 4, 57))
>q : Symbol(q, Decl(intersectionOfUnionNarrowing.ts, 6, 13))
>b : Symbol(b, Decl(intersectionOfUnionNarrowing.ts, 1, 24), Decl(intersectionOfUnionNarrowing.ts, 4, 57))
>bProp : Symbol(bProp, Decl(intersectionOfUnionNarrowing.ts, 2, 7))
}

View File

@@ -0,0 +1,44 @@
=== tests/cases/conformance/types/intersection/intersectionOfUnionNarrowing.ts ===
interface X {
a?: { aProp: string };
>a : { aProp: string; } | undefined
>aProp : string
b?: { bProp: string };
>b : { bProp: string; } | undefined
>bProp : string
}
type AorB = { a: object; b: undefined } | { a: undefined; b: object };
>AorB : AorB
>a : object
>b : undefined
>a : undefined
>b : object
declare const q: X & AorB;
>q : X & AorB
if (q.a !== undefined) {
>q.a !== undefined : boolean
>q.a : ({ aProp: string; } & object) | undefined
>q : X & AorB
>a : ({ aProp: string; } & object) | undefined
>undefined : undefined
q.a.aProp;
>q.a.aProp : string
>q.a : { aProp: string; } & object
>q : X & { a: object; b: undefined; }
>a : { aProp: string; } & object
>aProp : string
} else {
// q.b is previously incorrectly inferred as potentially undefined
q.b.bProp;
>q.b.bProp : string
>q.b : { bProp: string; } & object
>q : X & { a: undefined; b: object; }
>b : { bProp: string; } & object
>bProp : string
}

View File

@@ -0,0 +1,16 @@
// @strict: true
interface X {
a?: { aProp: string };
b?: { bProp: string };
}
type AorB = { a: object; b: undefined } | { a: undefined; b: object };
declare const q: X & AorB;
if (q.a !== undefined) {
q.a.aProp;
} else {
// q.b is previously incorrectly inferred as potentially undefined
q.b.bProp;
}