Skip to content

Commit 7bb5ba3

Browse files
authored
Discard types that reduce to never before discriminating by discriminable items (#62275)
1 parent f5ccf43 commit 7bb5ba3

File tree

4 files changed

+310
-1
lines changed

4 files changed

+310
-1
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24881,7 +24881,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2488124881

2488224882
function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: (readonly [() => Type, __String])[], related: (source: Type, target: Type) => boolean | Ternary) {
2488324883
const types = target.types;
24884-
const include: Ternary[] = types.map(t => t.flags & TypeFlags.Primitive ? Ternary.False : Ternary.True);
24884+
const include: Ternary[] = types.map(t => t.flags & TypeFlags.Primitive || getReducedType(t).flags & TypeFlags.Never ? Ternary.False : Ternary.True);
2488524885
for (const [getDiscriminatingType, propertyName] of discriminators) {
2488624886
// If the remaining target types include at least one with a matching discriminant, eliminate those that
2488724887
// have non-matching discriminants. This ensures that we ignore erroneous discriminators and gradually
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//// [tests/cases/compiler/contextuallyTypedByDiscriminableUnion2.ts] ////
2+
3+
=== contextuallyTypedByDiscriminableUnion2.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/62256
5+
6+
type Identifiable = { id: string };
7+
>Identifiable : Symbol(Identifiable, Decl(contextuallyTypedByDiscriminableUnion2.ts, 0, 0))
8+
>id : Symbol(id, Decl(contextuallyTypedByDiscriminableUnion2.ts, 2, 21))
9+
10+
interface EnableA {
11+
>EnableA : Symbol(EnableA, Decl(contextuallyTypedByDiscriminableUnion2.ts, 2, 35))
12+
13+
readonly enableA: true;
14+
>enableA : Symbol(EnableA.enableA, Decl(contextuallyTypedByDiscriminableUnion2.ts, 4, 19))
15+
16+
// this introduces a conflicting property with some of the other members of MyComponentProps
17+
// making relevant final union members reduced nevers
18+
readonly enableB: true;
19+
>enableB : Symbol(EnableA.enableB, Decl(contextuallyTypedByDiscriminableUnion2.ts, 5, 25))
20+
}
21+
22+
interface DisableA {
23+
>DisableA : Symbol(DisableA, Decl(contextuallyTypedByDiscriminableUnion2.ts, 9, 1))
24+
25+
readonly enableA?: false;
26+
>enableA : Symbol(DisableA.enableA, Decl(contextuallyTypedByDiscriminableUnion2.ts, 11, 20))
27+
}
28+
29+
interface EnableB {
30+
>EnableB : Symbol(EnableB, Decl(contextuallyTypedByDiscriminableUnion2.ts, 13, 1))
31+
32+
readonly enableB?: true;
33+
>enableB : Symbol(EnableB.enableB, Decl(contextuallyTypedByDiscriminableUnion2.ts, 15, 19))
34+
}
35+
36+
interface DisableB {
37+
>DisableB : Symbol(DisableB, Decl(contextuallyTypedByDiscriminableUnion2.ts, 17, 1))
38+
39+
readonly enableB: false;
40+
>enableB : Symbol(DisableB.enableB, Decl(contextuallyTypedByDiscriminableUnion2.ts, 19, 20))
41+
}
42+
43+
export interface EnableD<I extends Identifiable> {
44+
>EnableD : Symbol(EnableD, Decl(contextuallyTypedByDiscriminableUnion2.ts, 21, 1))
45+
>I : Symbol(I, Decl(contextuallyTypedByDiscriminableUnion2.ts, 23, 25))
46+
>Identifiable : Symbol(Identifiable, Decl(contextuallyTypedByDiscriminableUnion2.ts, 0, 0))
47+
48+
readonly enableD: true;
49+
>enableD : Symbol(EnableD.enableD, Decl(contextuallyTypedByDiscriminableUnion2.ts, 23, 50))
50+
51+
readonly value: I["id"] | null;
52+
>value : Symbol(EnableD.value, Decl(contextuallyTypedByDiscriminableUnion2.ts, 24, 25))
53+
>I : Symbol(I, Decl(contextuallyTypedByDiscriminableUnion2.ts, 23, 25))
54+
55+
readonly setItem: (item: I | null) => void;
56+
>setItem : Symbol(EnableD.setItem, Decl(contextuallyTypedByDiscriminableUnion2.ts, 25, 33))
57+
>item : Symbol(item, Decl(contextuallyTypedByDiscriminableUnion2.ts, 26, 21))
58+
>I : Symbol(I, Decl(contextuallyTypedByDiscriminableUnion2.ts, 23, 25))
59+
}
60+
61+
export interface DisableD<I extends Identifiable> {
62+
>DisableD : Symbol(DisableD, Decl(contextuallyTypedByDiscriminableUnion2.ts, 27, 1))
63+
>I : Symbol(I, Decl(contextuallyTypedByDiscriminableUnion2.ts, 29, 26))
64+
>Identifiable : Symbol(Identifiable, Decl(contextuallyTypedByDiscriminableUnion2.ts, 0, 0))
65+
66+
readonly enableD: false;
67+
>enableD : Symbol(DisableD.enableD, Decl(contextuallyTypedByDiscriminableUnion2.ts, 29, 51))
68+
69+
readonly value: I["id"];
70+
>value : Symbol(DisableD.value, Decl(contextuallyTypedByDiscriminableUnion2.ts, 30, 26))
71+
>I : Symbol(I, Decl(contextuallyTypedByDiscriminableUnion2.ts, 29, 26))
72+
73+
readonly setItem: (item: I) => void;
74+
>setItem : Symbol(DisableD.setItem, Decl(contextuallyTypedByDiscriminableUnion2.ts, 31, 26))
75+
>item : Symbol(item, Decl(contextuallyTypedByDiscriminableUnion2.ts, 32, 21))
76+
>I : Symbol(I, Decl(contextuallyTypedByDiscriminableUnion2.ts, 29, 26))
77+
}
78+
79+
type MyComponentProps<I extends Identifiable> = (EnableA | DisableA) &
80+
>MyComponentProps : Symbol(MyComponentProps, Decl(contextuallyTypedByDiscriminableUnion2.ts, 33, 1))
81+
>I : Symbol(I, Decl(contextuallyTypedByDiscriminableUnion2.ts, 35, 22))
82+
>Identifiable : Symbol(Identifiable, Decl(contextuallyTypedByDiscriminableUnion2.ts, 0, 0))
83+
>EnableA : Symbol(EnableA, Decl(contextuallyTypedByDiscriminableUnion2.ts, 2, 35))
84+
>DisableA : Symbol(DisableA, Decl(contextuallyTypedByDiscriminableUnion2.ts, 9, 1))
85+
86+
(EnableB | DisableB) &
87+
>EnableB : Symbol(EnableB, Decl(contextuallyTypedByDiscriminableUnion2.ts, 13, 1))
88+
>DisableB : Symbol(DisableB, Decl(contextuallyTypedByDiscriminableUnion2.ts, 17, 1))
89+
90+
(DisableD<I> | EnableD<I>);
91+
>DisableD : Symbol(DisableD, Decl(contextuallyTypedByDiscriminableUnion2.ts, 27, 1))
92+
>I : Symbol(I, Decl(contextuallyTypedByDiscriminableUnion2.ts, 35, 22))
93+
>EnableD : Symbol(EnableD, Decl(contextuallyTypedByDiscriminableUnion2.ts, 21, 1))
94+
>I : Symbol(I, Decl(contextuallyTypedByDiscriminableUnion2.ts, 35, 22))
95+
96+
const MyComponent = <I extends Identifiable>(props: MyComponentProps<I>) => {};
97+
>MyComponent : Symbol(MyComponent, Decl(contextuallyTypedByDiscriminableUnion2.ts, 39, 5))
98+
>I : Symbol(I, Decl(contextuallyTypedByDiscriminableUnion2.ts, 39, 21))
99+
>Identifiable : Symbol(Identifiable, Decl(contextuallyTypedByDiscriminableUnion2.ts, 0, 0))
100+
>props : Symbol(props, Decl(contextuallyTypedByDiscriminableUnion2.ts, 39, 45))
101+
>MyComponentProps : Symbol(MyComponentProps, Decl(contextuallyTypedByDiscriminableUnion2.ts, 33, 1))
102+
>I : Symbol(I, Decl(contextuallyTypedByDiscriminableUnion2.ts, 39, 21))
103+
104+
declare const item: string | null;
105+
>item : Symbol(item, Decl(contextuallyTypedByDiscriminableUnion2.ts, 41, 13))
106+
107+
MyComponent({
108+
>MyComponent : Symbol(MyComponent, Decl(contextuallyTypedByDiscriminableUnion2.ts, 39, 5))
109+
110+
enableD: true,
111+
>enableD : Symbol(enableD, Decl(contextuallyTypedByDiscriminableUnion2.ts, 43, 13))
112+
113+
value: item,
114+
>value : Symbol(value, Decl(contextuallyTypedByDiscriminableUnion2.ts, 44, 16))
115+
>item : Symbol(item, Decl(contextuallyTypedByDiscriminableUnion2.ts, 41, 13))
116+
117+
setItem: (item) => {},
118+
>setItem : Symbol(setItem, Decl(contextuallyTypedByDiscriminableUnion2.ts, 45, 14))
119+
>item : Symbol(item, Decl(contextuallyTypedByDiscriminableUnion2.ts, 46, 12))
120+
121+
});
122+
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//// [tests/cases/compiler/contextuallyTypedByDiscriminableUnion2.ts] ////
2+
3+
=== contextuallyTypedByDiscriminableUnion2.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/62256
5+
6+
type Identifiable = { id: string };
7+
>Identifiable : Identifiable
8+
> : ^^^^^^^^^^^^
9+
>id : string
10+
> : ^^^^^^
11+
12+
interface EnableA {
13+
readonly enableA: true;
14+
>enableA : true
15+
> : ^^^^
16+
>true : true
17+
> : ^^^^
18+
19+
// this introduces a conflicting property with some of the other members of MyComponentProps
20+
// making relevant final union members reduced nevers
21+
readonly enableB: true;
22+
>enableB : true
23+
> : ^^^^
24+
>true : true
25+
> : ^^^^
26+
}
27+
28+
interface DisableA {
29+
readonly enableA?: false;
30+
>enableA : false | undefined
31+
> : ^^^^^^^^^^^^^^^^^
32+
>false : false
33+
> : ^^^^^
34+
}
35+
36+
interface EnableB {
37+
readonly enableB?: true;
38+
>enableB : true | undefined
39+
> : ^^^^^^^^^^^^^^^^
40+
>true : true
41+
> : ^^^^
42+
}
43+
44+
interface DisableB {
45+
readonly enableB: false;
46+
>enableB : false
47+
> : ^^^^^
48+
>false : false
49+
> : ^^^^^
50+
}
51+
52+
export interface EnableD<I extends Identifiable> {
53+
readonly enableD: true;
54+
>enableD : true
55+
> : ^^^^
56+
>true : true
57+
> : ^^^^
58+
59+
readonly value: I["id"] | null;
60+
>value : I["id"] | null
61+
> : ^^^^^^^^^^^^^^
62+
63+
readonly setItem: (item: I | null) => void;
64+
>setItem : (item: I | null) => void
65+
> : ^ ^^ ^^^^^
66+
>item : I | null
67+
> : ^^^^^^^^
68+
}
69+
70+
export interface DisableD<I extends Identifiable> {
71+
readonly enableD: false;
72+
>enableD : false
73+
> : ^^^^^
74+
>false : false
75+
> : ^^^^^
76+
77+
readonly value: I["id"];
78+
>value : I["id"]
79+
> : ^^^^^^^
80+
81+
readonly setItem: (item: I) => void;
82+
>setItem : (item: I) => void
83+
> : ^ ^^ ^^^^^
84+
>item : I
85+
> : ^
86+
}
87+
88+
type MyComponentProps<I extends Identifiable> = (EnableA | DisableA) &
89+
>MyComponentProps : (EnableA & EnableB & DisableD<I>) | (EnableA & EnableB & EnableD<I>) | (DisableA & EnableB & DisableD<I>) | (DisableA & EnableB & EnableD<I>) | (DisableA & DisableB & DisableD<I>) | (DisableA & DisableB & EnableD<I>)
90+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
91+
92+
(EnableB | DisableB) &
93+
(DisableD<I> | EnableD<I>);
94+
95+
const MyComponent = <I extends Identifiable>(props: MyComponentProps<I>) => {};
96+
>MyComponent : <I extends Identifiable>(props: MyComponentProps<I>) => void
97+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^
98+
><I extends Identifiable>(props: MyComponentProps<I>) => {} : <I extends Identifiable>(props: MyComponentProps<I>) => void
99+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^
100+
>props : (EnableA & EnableB & DisableD<I>) | (EnableA & EnableB & EnableD<I>) | (DisableA & EnableB & DisableD<I>) | (DisableA & EnableB & EnableD<I>) | (DisableA & DisableB & DisableD<I>) | (DisableA & DisableB & EnableD<I>)
101+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
102+
103+
declare const item: string | null;
104+
>item : string | null
105+
> : ^^^^^^^^^^^^^
106+
107+
MyComponent({
108+
>MyComponent({ enableD: true, value: item, setItem: (item) => {},}) : void
109+
> : ^^^^
110+
>MyComponent : <I extends Identifiable>(props: MyComponentProps<I>) => void
111+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^
112+
>{ enableD: true, value: item, setItem: (item) => {},} : { enableD: true; value: string | null; setItem: (item: Identifiable | null) => void; }
113+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
114+
115+
enableD: true,
116+
>enableD : true
117+
> : ^^^^
118+
>true : true
119+
> : ^^^^
120+
121+
value: item,
122+
>value : string | null
123+
> : ^^^^^^^^^^^^^
124+
>item : string | null
125+
> : ^^^^^^^^^^^^^
126+
127+
setItem: (item) => {},
128+
>setItem : (item: Identifiable | null) => void
129+
> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
130+
>(item) => {} : (item: Identifiable | null) => void
131+
> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
132+
>item : Identifiable | null
133+
> : ^^^^^^^^^^^^^^^^^^^
134+
135+
});
136+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// https://github.com/microsoft/TypeScript/issues/62256
5+
6+
type Identifiable = { id: string };
7+
8+
interface EnableA {
9+
readonly enableA: true;
10+
// this introduces a conflicting property with some of the other members of MyComponentProps
11+
// making relevant final union members reduced nevers
12+
readonly enableB: true;
13+
}
14+
15+
interface DisableA {
16+
readonly enableA?: false;
17+
}
18+
19+
interface EnableB {
20+
readonly enableB?: true;
21+
}
22+
23+
interface DisableB {
24+
readonly enableB: false;
25+
}
26+
27+
export interface EnableD<I extends Identifiable> {
28+
readonly enableD: true;
29+
readonly value: I["id"] | null;
30+
readonly setItem: (item: I | null) => void;
31+
}
32+
33+
export interface DisableD<I extends Identifiable> {
34+
readonly enableD: false;
35+
readonly value: I["id"];
36+
readonly setItem: (item: I) => void;
37+
}
38+
39+
type MyComponentProps<I extends Identifiable> = (EnableA | DisableA) &
40+
(EnableB | DisableB) &
41+
(DisableD<I> | EnableD<I>);
42+
43+
const MyComponent = <I extends Identifiable>(props: MyComponentProps<I>) => {};
44+
45+
declare const item: string | null;
46+
47+
MyComponent({
48+
enableD: true,
49+
value: item,
50+
setItem: (item) => {},
51+
});

0 commit comments

Comments
 (0)