Skip to content

Commit a329210

Browse files
authored
Fixed a regression with discriminating unions based on a union property against undefined with strictNullChecks: false (#49648)
* Fixed a regression with discriminating unions based on a union property against `undefined` with `strictNullChecks: false` * Add additional test case from the issue comment
1 parent 7b0df1f commit a329210

4 files changed

+205
-1
lines changed

src/compiler/checker.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -18986,7 +18986,8 @@ namespace ts {
1898618986
// Before normalization: if `source` is type an object type, and `target` is primitive,
1898718987
// skip all the checks we don't need and just return `isSimpleTypeRelatedTo` result
1898818988
if (originalSource.flags & TypeFlags.Object && originalTarget.flags & TypeFlags.Primitive) {
18989-
if (isSimpleTypeRelatedTo(originalSource, originalTarget, relation, reportErrors ? reportError : undefined)) {
18989+
if (relation === comparableRelation && !(originalTarget.flags & TypeFlags.Never) && isSimpleTypeRelatedTo(originalTarget, originalSource, relation) ||
18990+
isSimpleTypeRelatedTo(originalSource, originalTarget, relation, reportErrors ? reportError : undefined)) {
1899018991
return Ternary.True;
1899118992
}
1899218993
if (reportErrors) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
=== tests/cases/compiler/discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts ===
2+
// strictNullChecks: false
3+
4+
// repro #49643
5+
6+
interface A {}
7+
>A : Symbol(A, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 0, 0))
8+
9+
interface B {}
10+
>B : Symbol(B, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 4, 14))
11+
12+
declare let opts:
13+
>opts : Symbol(opts, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 7, 11))
14+
15+
| { objectRef?: undefined; getObjectRef: () => any }
16+
>objectRef : Symbol(objectRef, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 8, 6))
17+
>getObjectRef : Symbol(getObjectRef, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 8, 29))
18+
19+
| { objectRef: A | B; getObjectRef?: undefined };
20+
>objectRef : Symbol(objectRef, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 9, 6))
21+
>A : Symbol(A, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 0, 0))
22+
>B : Symbol(B, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 4, 14))
23+
>getObjectRef : Symbol(getObjectRef, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 9, 24))
24+
25+
opts.objectRef || opts.getObjectRef();
26+
>opts.objectRef : Symbol(objectRef, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 8, 6), Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 9, 6))
27+
>opts : Symbol(opts, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 7, 11))
28+
>objectRef : Symbol(objectRef, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 8, 6), Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 9, 6))
29+
>opts.getObjectRef : Symbol(getObjectRef, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 8, 29), Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 9, 24))
30+
>opts : Symbol(opts, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 7, 11))
31+
>getObjectRef : Symbol(getObjectRef, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 8, 29), Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 9, 24))
32+
33+
// repro #49643 issuecomment-1174455723
34+
35+
interface X {
36+
>X : Symbol(X, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 11, 38))
37+
38+
foo: string;
39+
>foo : Symbol(X.foo, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 15, 13))
40+
}
41+
42+
interface Y {
43+
>Y : Symbol(Y, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 17, 1))
44+
45+
baz: number;
46+
>baz : Symbol(Y.baz, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 19, 13))
47+
}
48+
49+
interface A2 {
50+
>A2 : Symbol(A2, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 21, 1))
51+
52+
result: unknown;
53+
>result : Symbol(A2.result, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 23, 14))
54+
55+
error: undefined;
56+
>error : Symbol(A2.error, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 24, 20))
57+
}
58+
59+
interface B2 {
60+
>B2 : Symbol(B2, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 26, 1))
61+
62+
error: X | Y;
63+
>error : Symbol(B2.error, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 28, 14))
64+
>X : Symbol(X, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 11, 38))
65+
>Y : Symbol(Y, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 17, 1))
66+
}
67+
68+
const testMethod = (m: A2 | B2) => {
69+
>testMethod : Symbol(testMethod, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 32, 5))
70+
>m : Symbol(m, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 32, 20))
71+
>A2 : Symbol(A2, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 21, 1))
72+
>B2 : Symbol(B2, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 26, 1))
73+
74+
if (m.error) {
75+
>m.error : Symbol(error, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 24, 20), Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 28, 14))
76+
>m : Symbol(m, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 32, 20))
77+
>error : Symbol(error, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 24, 20), Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 28, 14))
78+
79+
m; // should be A2 | B2
80+
>m : Symbol(m, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 32, 20))
81+
82+
} else {
83+
m; // should be A2 | B2
84+
>m : Symbol(m, Decl(discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts, 32, 20))
85+
}
86+
}
87+
88+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
=== tests/cases/compiler/discriminatingUnionWithUnionPropertyAgainstUndefinedWithoutStrictNullChecks.ts ===
2+
// strictNullChecks: false
3+
4+
// repro #49643
5+
6+
interface A {}
7+
interface B {}
8+
9+
declare let opts:
10+
>opts : { objectRef?: undefined; getObjectRef: () => any; } | { objectRef: A | B; getObjectRef?: undefined; }
11+
12+
| { objectRef?: undefined; getObjectRef: () => any }
13+
>objectRef : undefined
14+
>getObjectRef : () => any
15+
16+
| { objectRef: A | B; getObjectRef?: undefined };
17+
>objectRef : A | B
18+
>getObjectRef : undefined
19+
20+
opts.objectRef || opts.getObjectRef();
21+
>opts.objectRef || opts.getObjectRef() : any
22+
>opts.objectRef : A | B
23+
>opts : { objectRef?: undefined; getObjectRef: () => any; } | { objectRef: A | B; getObjectRef?: undefined; }
24+
>objectRef : A | B
25+
>opts.getObjectRef() : any
26+
>opts.getObjectRef : () => any
27+
>opts : { objectRef?: undefined; getObjectRef: () => any; } | { objectRef: A | B; getObjectRef?: undefined; }
28+
>getObjectRef : () => any
29+
30+
// repro #49643 issuecomment-1174455723
31+
32+
interface X {
33+
foo: string;
34+
>foo : string
35+
}
36+
37+
interface Y {
38+
baz: number;
39+
>baz : number
40+
}
41+
42+
interface A2 {
43+
result: unknown;
44+
>result : unknown
45+
46+
error: undefined;
47+
>error : undefined
48+
}
49+
50+
interface B2 {
51+
error: X | Y;
52+
>error : X | Y
53+
}
54+
55+
const testMethod = (m: A2 | B2) => {
56+
>testMethod : (m: A2 | B2) => void
57+
>(m: A2 | B2) => { if (m.error) { m; // should be A2 | B2 } else { m; // should be A2 | B2 }} : (m: A2 | B2) => void
58+
>m : A2 | B2
59+
60+
if (m.error) {
61+
>m.error : X | Y
62+
>m : A2 | B2
63+
>error : X | Y
64+
65+
m; // should be A2 | B2
66+
>m : A2 | B2
67+
68+
} else {
69+
m; // should be A2 | B2
70+
>m : A2 | B2
71+
}
72+
}
73+
74+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// @noEmit: true
2+
// strictNullChecks: false
3+
4+
// repro #49643
5+
6+
interface A {}
7+
interface B {}
8+
9+
declare let opts:
10+
| { objectRef?: undefined; getObjectRef: () => any }
11+
| { objectRef: A | B; getObjectRef?: undefined };
12+
13+
opts.objectRef || opts.getObjectRef();
14+
15+
// repro #49643 issuecomment-1174455723
16+
17+
interface X {
18+
foo: string;
19+
}
20+
21+
interface Y {
22+
baz: number;
23+
}
24+
25+
interface A2 {
26+
result: unknown;
27+
error: undefined;
28+
}
29+
30+
interface B2 {
31+
error: X | Y;
32+
}
33+
34+
const testMethod = (m: A2 | B2) => {
35+
if (m.error) {
36+
m; // should be A2 | B2
37+
} else {
38+
m; // should be A2 | B2
39+
}
40+
}
41+

0 commit comments

Comments
 (0)