Skip to content

Commit acdf62f

Browse files
authored
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.
1 parent c17fd8c commit acdf62f

File tree

5 files changed

+134
-1
lines changed

5 files changed

+134
-1
lines changed

src/compiler/checker.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18363,7 +18363,7 @@ namespace ts {
1836318363
// parameter 'T extends 1 | 2', the intersection 'T & 1' should be reduced to '1' such that it doesn't
1836418364
// appear to be comparable to '2'.
1836518365
if (relation === comparableRelation && target.flags & TypeFlags.Primitive) {
18366-
const constraints = sameMap((source as IntersectionType).types, t => t.flags & TypeFlags.Primitive ? t : getBaseConstraintOfType(t) || unknownType);
18366+
const constraints = sameMap((source as IntersectionType).types, getBaseConstraintOrType);
1836718367
if (constraints !== (source as IntersectionType).types) {
1836818368
source = getIntersectionType(constraints);
1836918369
if (!(source.flags & TypeFlags.Intersection)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//// [intersectionOfUnionNarrowing.ts]
2+
interface X {
3+
a?: { aProp: string };
4+
b?: { bProp: string };
5+
}
6+
type AorB = { a: object; b: undefined } | { a: undefined; b: object };
7+
8+
declare const q: X & AorB;
9+
10+
if (q.a !== undefined) {
11+
q.a.aProp;
12+
} else {
13+
// q.b is previously incorrectly inferred as potentially undefined
14+
q.b.bProp;
15+
}
16+
17+
18+
//// [intersectionOfUnionNarrowing.js]
19+
"use strict";
20+
if (q.a !== undefined) {
21+
q.a.aProp;
22+
}
23+
else {
24+
// q.b is previously incorrectly inferred as potentially undefined
25+
q.b.bProp;
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== tests/cases/conformance/types/intersection/intersectionOfUnionNarrowing.ts ===
2+
interface X {
3+
>X : Symbol(X, Decl(intersectionOfUnionNarrowing.ts, 0, 0))
4+
5+
a?: { aProp: string };
6+
>a : Symbol(X.a, Decl(intersectionOfUnionNarrowing.ts, 0, 13))
7+
>aProp : Symbol(aProp, Decl(intersectionOfUnionNarrowing.ts, 1, 7))
8+
9+
b?: { bProp: string };
10+
>b : Symbol(X.b, Decl(intersectionOfUnionNarrowing.ts, 1, 24))
11+
>bProp : Symbol(bProp, Decl(intersectionOfUnionNarrowing.ts, 2, 7))
12+
}
13+
type AorB = { a: object; b: undefined } | { a: undefined; b: object };
14+
>AorB : Symbol(AorB, Decl(intersectionOfUnionNarrowing.ts, 3, 1))
15+
>a : Symbol(a, Decl(intersectionOfUnionNarrowing.ts, 4, 13))
16+
>b : Symbol(b, Decl(intersectionOfUnionNarrowing.ts, 4, 24))
17+
>a : Symbol(a, Decl(intersectionOfUnionNarrowing.ts, 4, 43))
18+
>b : Symbol(b, Decl(intersectionOfUnionNarrowing.ts, 4, 57))
19+
20+
declare const q: X & AorB;
21+
>q : Symbol(q, Decl(intersectionOfUnionNarrowing.ts, 6, 13))
22+
>X : Symbol(X, Decl(intersectionOfUnionNarrowing.ts, 0, 0))
23+
>AorB : Symbol(AorB, Decl(intersectionOfUnionNarrowing.ts, 3, 1))
24+
25+
if (q.a !== undefined) {
26+
>q.a : Symbol(a, Decl(intersectionOfUnionNarrowing.ts, 0, 13), Decl(intersectionOfUnionNarrowing.ts, 4, 13), Decl(intersectionOfUnionNarrowing.ts, 0, 13), Decl(intersectionOfUnionNarrowing.ts, 4, 43))
27+
>q : Symbol(q, Decl(intersectionOfUnionNarrowing.ts, 6, 13))
28+
>a : Symbol(a, Decl(intersectionOfUnionNarrowing.ts, 0, 13), Decl(intersectionOfUnionNarrowing.ts, 4, 13), Decl(intersectionOfUnionNarrowing.ts, 0, 13), Decl(intersectionOfUnionNarrowing.ts, 4, 43))
29+
>undefined : Symbol(undefined)
30+
31+
q.a.aProp;
32+
>q.a.aProp : Symbol(aProp, Decl(intersectionOfUnionNarrowing.ts, 1, 7))
33+
>q.a : Symbol(a, Decl(intersectionOfUnionNarrowing.ts, 0, 13), Decl(intersectionOfUnionNarrowing.ts, 4, 13))
34+
>q : Symbol(q, Decl(intersectionOfUnionNarrowing.ts, 6, 13))
35+
>a : Symbol(a, Decl(intersectionOfUnionNarrowing.ts, 0, 13), Decl(intersectionOfUnionNarrowing.ts, 4, 13))
36+
>aProp : Symbol(aProp, Decl(intersectionOfUnionNarrowing.ts, 1, 7))
37+
38+
} else {
39+
// q.b is previously incorrectly inferred as potentially undefined
40+
q.b.bProp;
41+
>q.b.bProp : Symbol(bProp, Decl(intersectionOfUnionNarrowing.ts, 2, 7))
42+
>q.b : Symbol(b, Decl(intersectionOfUnionNarrowing.ts, 1, 24), Decl(intersectionOfUnionNarrowing.ts, 4, 57))
43+
>q : Symbol(q, Decl(intersectionOfUnionNarrowing.ts, 6, 13))
44+
>b : Symbol(b, Decl(intersectionOfUnionNarrowing.ts, 1, 24), Decl(intersectionOfUnionNarrowing.ts, 4, 57))
45+
>bProp : Symbol(bProp, Decl(intersectionOfUnionNarrowing.ts, 2, 7))
46+
}
47+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== tests/cases/conformance/types/intersection/intersectionOfUnionNarrowing.ts ===
2+
interface X {
3+
a?: { aProp: string };
4+
>a : { aProp: string; } | undefined
5+
>aProp : string
6+
7+
b?: { bProp: string };
8+
>b : { bProp: string; } | undefined
9+
>bProp : string
10+
}
11+
type AorB = { a: object; b: undefined } | { a: undefined; b: object };
12+
>AorB : AorB
13+
>a : object
14+
>b : undefined
15+
>a : undefined
16+
>b : object
17+
18+
declare const q: X & AorB;
19+
>q : X & AorB
20+
21+
if (q.a !== undefined) {
22+
>q.a !== undefined : boolean
23+
>q.a : ({ aProp: string; } & object) | undefined
24+
>q : X & AorB
25+
>a : ({ aProp: string; } & object) | undefined
26+
>undefined : undefined
27+
28+
q.a.aProp;
29+
>q.a.aProp : string
30+
>q.a : { aProp: string; } & object
31+
>q : X & { a: object; b: undefined; }
32+
>a : { aProp: string; } & object
33+
>aProp : string
34+
35+
} else {
36+
// q.b is previously incorrectly inferred as potentially undefined
37+
q.b.bProp;
38+
>q.b.bProp : string
39+
>q.b : { bProp: string; } & object
40+
>q : X & { a: undefined; b: object; }
41+
>b : { bProp: string; } & object
42+
>bProp : string
43+
}
44+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @strict: true
2+
3+
interface X {
4+
a?: { aProp: string };
5+
b?: { bProp: string };
6+
}
7+
type AorB = { a: object; b: undefined } | { a: undefined; b: object };
8+
9+
declare const q: X & AorB;
10+
11+
if (q.a !== undefined) {
12+
q.a.aProp;
13+
} else {
14+
// q.b is previously incorrectly inferred as potentially undefined
15+
q.b.bProp;
16+
}

0 commit comments

Comments
 (0)