Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7d5097b

Browse files
committedMay 9, 2025·
Compare optional property flag when comparing against discriminant properties under exactOptionalPropertyTypes
1 parent 8c62e08 commit 7d5097b

15 files changed

+1103
-1
lines changed
 

‎src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23998,7 +23998,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2399823998
if (!targetProperty) continue outer;
2399923999
if (sourceProperty === targetProperty) continue;
2400024000
// We compare the source property to the target in the context of a single discriminant type.
24001-
const related = propertyRelatedTo(source, target, sourceProperty, targetProperty, _ => combination[i], /*reportErrors*/ false, IntersectionState.None, /*skipOptional*/ strictNullChecks || relation === comparableRelation);
24001+
const related = propertyRelatedTo(source, target, sourceProperty, targetProperty, _ => combination[i], /*reportErrors*/ false, IntersectionState.None, /*skipOptional*/ strictNullChecks && !exactOptionalPropertyTypes || relation === comparableRelation);
2400224002
// If the target property could not be found, or if the properties were not related,
2400324003
// then this constituent is not a match.
2400424004
if (!related) {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
unionRelationshipCheckPasses2.ts(10,3): error TS2322: Type '{ type: "A"; value?: undefined; } | { type: "B"; value: string; }' is not assignable to type 'U'.
2+
Type '{ type: "A"; value?: undefined; }' is not assignable to type 'U'.
3+
Type '{ type: "A"; value?: undefined; }' is not assignable to type '{ type: "A"; value: null; }'.
4+
Types of property 'value' are incompatible.
5+
Type 'undefined' is not assignable to type 'null'.
6+
unionRelationshipCheckPasses2.ts(21,5): error TS2322: Type '{ type: "A"; }' is not assignable to type 'U'.
7+
Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: null; }'.
8+
9+
10+
==== unionRelationshipCheckPasses2.ts (2 errors) ====
11+
// https://github.com/microsoft/TypeScript/issues/61678
12+
13+
export type U = { type: "A"; value: null } | { type: "B"; value: string };
14+
15+
function call<T>(f: () => T): T {
16+
return f();
17+
}
18+
19+
export function functionCall(): U {
20+
return call(() => { // error
21+
~~~~~~
22+
!!! error TS2322: Type '{ type: "A"; value?: undefined; } | { type: "B"; value: string; }' is not assignable to type 'U'.
23+
!!! error TS2322: Type '{ type: "A"; value?: undefined; }' is not assignable to type 'U'.
24+
!!! error TS2322: Type '{ type: "A"; value?: undefined; }' is not assignable to type '{ type: "A"; value: null; }'.
25+
!!! error TS2322: Types of property 'value' are incompatible.
26+
!!! error TS2322: Type 'undefined' is not assignable to type 'null'.
27+
if (Math.random()) {
28+
return { type: "A" };
29+
}
30+
31+
return { type: "B", value: "test" };
32+
});
33+
}
34+
35+
export function directReturn(): U {
36+
if (Math.random()) {
37+
return { type: "A" }; // error
38+
~~~~~~
39+
!!! error TS2322: Type '{ type: "A"; }' is not assignable to type 'U'.
40+
!!! error TS2322: Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: null; }'.
41+
!!! related TS2728 unionRelationshipCheckPasses2.ts:3:30: 'value' is declared here.
42+
}
43+
44+
return { type: "B", value: "test" };
45+
}
46+
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//// [tests/cases/compiler/unionRelationshipCheckPasses2.ts] ////
2+
3+
=== unionRelationshipCheckPasses2.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/61678
5+
6+
export type U = { type: "A"; value: null } | { type: "B"; value: string };
7+
>U : Symbol(U, Decl(unionRelationshipCheckPasses2.ts, 0, 0))
8+
>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 2, 17))
9+
>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 2, 28))
10+
>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 2, 46))
11+
>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 2, 57))
12+
13+
function call<T>(f: () => T): T {
14+
>call : Symbol(call, Decl(unionRelationshipCheckPasses2.ts, 2, 74))
15+
>T : Symbol(T, Decl(unionRelationshipCheckPasses2.ts, 4, 14))
16+
>f : Symbol(f, Decl(unionRelationshipCheckPasses2.ts, 4, 17))
17+
>T : Symbol(T, Decl(unionRelationshipCheckPasses2.ts, 4, 14))
18+
>T : Symbol(T, Decl(unionRelationshipCheckPasses2.ts, 4, 14))
19+
20+
return f();
21+
>f : Symbol(f, Decl(unionRelationshipCheckPasses2.ts, 4, 17))
22+
}
23+
24+
export function functionCall(): U {
25+
>functionCall : Symbol(functionCall, Decl(unionRelationshipCheckPasses2.ts, 6, 1))
26+
>U : Symbol(U, Decl(unionRelationshipCheckPasses2.ts, 0, 0))
27+
28+
return call(() => { // error
29+
>call : Symbol(call, Decl(unionRelationshipCheckPasses2.ts, 2, 74))
30+
31+
if (Math.random()) {
32+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
33+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
34+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
35+
36+
return { type: "A" };
37+
>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 11, 14))
38+
}
39+
40+
return { type: "B", value: "test" };
41+
>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 14, 12))
42+
>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 14, 23))
43+
44+
});
45+
}
46+
47+
export function directReturn(): U {
48+
>directReturn : Symbol(directReturn, Decl(unionRelationshipCheckPasses2.ts, 16, 1))
49+
>U : Symbol(U, Decl(unionRelationshipCheckPasses2.ts, 0, 0))
50+
51+
if (Math.random()) {
52+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
53+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
54+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
55+
56+
return { type: "A" }; // error
57+
>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 20, 12))
58+
}
59+
60+
return { type: "B", value: "test" };
61+
>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 23, 10))
62+
>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 23, 21))
63+
}
64+
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//// [tests/cases/compiler/unionRelationshipCheckPasses2.ts] ////
2+
3+
=== unionRelationshipCheckPasses2.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/61678
5+
6+
export type U = { type: "A"; value: null } | { type: "B"; value: string };
7+
>U : U
8+
> : ^
9+
>type : "A"
10+
> : ^^^
11+
>value : null
12+
> : ^^^^
13+
>type : "B"
14+
> : ^^^
15+
>value : string
16+
> : ^^^^^^
17+
18+
function call<T>(f: () => T): T {
19+
>call : <T>(f: () => T) => T
20+
> : ^ ^^ ^^ ^^^^^
21+
>f : () => T
22+
> : ^^^^^^
23+
24+
return f();
25+
>f() : T
26+
> : ^
27+
>f : () => T
28+
> : ^^^^^^
29+
}
30+
31+
export function functionCall(): U {
32+
>functionCall : () => U
33+
> : ^^^^^^
34+
35+
return call(() => { // error
36+
>call(() => { // error if (Math.random()) { return { type: "A" }; } return { type: "B", value: "test" }; }) : { type: "A"; value?: undefined; } | { type: "B"; value: string; }
37+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
38+
>call : <T>(f: () => T) => T
39+
> : ^ ^^ ^^ ^^^^^
40+
>() => { // error if (Math.random()) { return { type: "A" }; } return { type: "B", value: "test" }; } : () => { type: "A"; value?: undefined; } | { type: "B"; value: string; }
41+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
42+
43+
if (Math.random()) {
44+
>Math.random() : number
45+
> : ^^^^^^
46+
>Math.random : () => number
47+
> : ^^^^^^
48+
>Math : Math
49+
> : ^^^^
50+
>random : () => number
51+
> : ^^^^^^
52+
53+
return { type: "A" };
54+
>{ type: "A" } : { type: "A"; }
55+
> : ^^^^^^^^^^^^^^
56+
>type : "A"
57+
> : ^^^
58+
>"A" : "A"
59+
> : ^^^
60+
}
61+
62+
return { type: "B", value: "test" };
63+
>{ type: "B", value: "test" } : { type: "B"; value: string; }
64+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
65+
>type : "B"
66+
> : ^^^
67+
>"B" : "B"
68+
> : ^^^
69+
>value : string
70+
> : ^^^^^^
71+
>"test" : "test"
72+
> : ^^^^^^
73+
74+
});
75+
}
76+
77+
export function directReturn(): U {
78+
>directReturn : () => U
79+
> : ^^^^^^
80+
81+
if (Math.random()) {
82+
>Math.random() : number
83+
> : ^^^^^^
84+
>Math.random : () => number
85+
> : ^^^^^^
86+
>Math : Math
87+
> : ^^^^
88+
>random : () => number
89+
> : ^^^^^^
90+
91+
return { type: "A" }; // error
92+
>{ type: "A" } : { type: "A"; }
93+
> : ^^^^^^^^^^^^^^
94+
>type : "A"
95+
> : ^^^
96+
>"A" : "A"
97+
> : ^^^
98+
}
99+
100+
return { type: "B", value: "test" };
101+
>{ type: "B", value: "test" } : { type: "B"; value: string; }
102+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
103+
>type : "B"
104+
> : ^^^
105+
>"B" : "B"
106+
> : ^^^
107+
>value : string
108+
> : ^^^^^^
109+
>"test" : "test"
110+
> : ^^^^^^
111+
}
112+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
unionRelationshipCheckPasses2.ts(10,3): error TS2322: Type '{ type: "A"; value?: never; } | { type: "B"; value: string; }' is not assignable to type 'U'.
2+
Type '{ type: "A"; value?: never; }' is not assignable to type 'U'.
3+
Type '{ type: "A"; value?: never; }' is not assignable to type '{ type: "A"; value: null; }'.
4+
Property 'value' is optional in type '{ type: "A"; value?: never; }' but required in type '{ type: "A"; value: null; }'.
5+
unionRelationshipCheckPasses2.ts(21,5): error TS2322: Type '{ type: "A"; }' is not assignable to type 'U'.
6+
Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: null; }'.
7+
8+
9+
==== unionRelationshipCheckPasses2.ts (2 errors) ====
10+
// https://github.com/microsoft/TypeScript/issues/61678
11+
12+
export type U = { type: "A"; value: null } | { type: "B"; value: string };
13+
14+
function call<T>(f: () => T): T {
15+
return f();
16+
}
17+
18+
export function functionCall(): U {
19+
return call(() => { // error
20+
~~~~~~
21+
!!! error TS2322: Type '{ type: "A"; value?: never; } | { type: "B"; value: string; }' is not assignable to type 'U'.
22+
!!! error TS2322: Type '{ type: "A"; value?: never; }' is not assignable to type 'U'.
23+
!!! error TS2322: Type '{ type: "A"; value?: never; }' is not assignable to type '{ type: "A"; value: null; }'.
24+
!!! error TS2322: Property 'value' is optional in type '{ type: "A"; value?: never; }' but required in type '{ type: "A"; value: null; }'.
25+
if (Math.random()) {
26+
return { type: "A" };
27+
}
28+
29+
return { type: "B", value: "test" };
30+
});
31+
}
32+
33+
export function directReturn(): U {
34+
if (Math.random()) {
35+
return { type: "A" }; // error
36+
~~~~~~
37+
!!! error TS2322: Type '{ type: "A"; }' is not assignable to type 'U'.
38+
!!! error TS2322: Property 'value' is missing in type '{ type: "A"; }' but required in type '{ type: "A"; value: null; }'.
39+
!!! related TS2728 unionRelationshipCheckPasses2.ts:3:30: 'value' is declared here.
40+
}
41+
42+
return { type: "B", value: "test" };
43+
}
44+
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//// [tests/cases/compiler/unionRelationshipCheckPasses2.ts] ////
2+
3+
=== unionRelationshipCheckPasses2.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/61678
5+
6+
export type U = { type: "A"; value: null } | { type: "B"; value: string };
7+
>U : Symbol(U, Decl(unionRelationshipCheckPasses2.ts, 0, 0))
8+
>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 2, 17))
9+
>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 2, 28))
10+
>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 2, 46))
11+
>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 2, 57))
12+
13+
function call<T>(f: () => T): T {
14+
>call : Symbol(call, Decl(unionRelationshipCheckPasses2.ts, 2, 74))
15+
>T : Symbol(T, Decl(unionRelationshipCheckPasses2.ts, 4, 14))
16+
>f : Symbol(f, Decl(unionRelationshipCheckPasses2.ts, 4, 17))
17+
>T : Symbol(T, Decl(unionRelationshipCheckPasses2.ts, 4, 14))
18+
>T : Symbol(T, Decl(unionRelationshipCheckPasses2.ts, 4, 14))
19+
20+
return f();
21+
>f : Symbol(f, Decl(unionRelationshipCheckPasses2.ts, 4, 17))
22+
}
23+
24+
export function functionCall(): U {
25+
>functionCall : Symbol(functionCall, Decl(unionRelationshipCheckPasses2.ts, 6, 1))
26+
>U : Symbol(U, Decl(unionRelationshipCheckPasses2.ts, 0, 0))
27+
28+
return call(() => { // error
29+
>call : Symbol(call, Decl(unionRelationshipCheckPasses2.ts, 2, 74))
30+
31+
if (Math.random()) {
32+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
33+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
34+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
35+
36+
return { type: "A" };
37+
>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 11, 14))
38+
}
39+
40+
return { type: "B", value: "test" };
41+
>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 14, 12))
42+
>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 14, 23))
43+
44+
});
45+
}
46+
47+
export function directReturn(): U {
48+
>directReturn : Symbol(directReturn, Decl(unionRelationshipCheckPasses2.ts, 16, 1))
49+
>U : Symbol(U, Decl(unionRelationshipCheckPasses2.ts, 0, 0))
50+
51+
if (Math.random()) {
52+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
53+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
54+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
55+
56+
return { type: "A" }; // error
57+
>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 20, 12))
58+
}
59+
60+
return { type: "B", value: "test" };
61+
>type : Symbol(type, Decl(unionRelationshipCheckPasses2.ts, 23, 10))
62+
>value : Symbol(value, Decl(unionRelationshipCheckPasses2.ts, 23, 21))
63+
}
64+

0 commit comments

Comments
 (0)