Skip to content

Commit 7e04e77

Browse files
authored
Improve type discrimination algorithm (#1085)
1 parent b28944e commit 7e04e77

File tree

7 files changed

+219
-26
lines changed

7 files changed

+219
-26
lines changed

internal/checker/relater.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,10 +1217,12 @@ func (c *Checker) discriminateTypeByDiscriminableItems(target *Type, discriminat
12171217
for i := range types {
12181218
if include[i] != TernaryFalse {
12191219
targetType := c.getTypeOfPropertyOrIndexSignatureOfType(types[i], discriminator.name(n))
1220-
if targetType != nil && discriminator.matches(n, targetType) {
1221-
matched = true
1222-
} else {
1223-
include[i] = TernaryMaybe
1220+
if targetType != nil {
1221+
if discriminator.matches(n, targetType) {
1222+
matched = true
1223+
} else {
1224+
include[i] = TernaryMaybe
1225+
}
12241226
}
12251227
}
12261228
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
missingDiscriminants.ts(17,23): error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'.
2+
missingDiscriminants.ts(18,34): error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'.
3+
4+
5+
==== missingDiscriminants.ts (2 errors) ====
6+
// https://github.com/microsoft/typescript-go/issues/1020
7+
8+
type Thing =
9+
| { str: "a", num: 0 }
10+
| { str: "b" }
11+
| { num: 1 }
12+
13+
const thing1: Thing = { str: "a", num: 0 }
14+
const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error
15+
const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error
16+
17+
type Item =
18+
| { kind: "a", subkind: 0, value: string }
19+
| { kind: "a", subkind: 1, value: number }
20+
| { kind: "b" }
21+
22+
const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a"
23+
~~~~~~~
24+
!!! error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'.
25+
const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property
26+
~~~~~~~
27+
!!! error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'.
28+
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//// [tests/cases/compiler/missingDiscriminants.ts] ////
2+
3+
=== missingDiscriminants.ts ===
4+
// https://github.com/microsoft/typescript-go/issues/1020
5+
6+
type Thing =
7+
>Thing : Symbol(Thing, Decl(missingDiscriminants.ts, 0, 0))
8+
9+
| { str: "a", num: 0 }
10+
>str : Symbol(str, Decl(missingDiscriminants.ts, 3, 5))
11+
>num : Symbol(num, Decl(missingDiscriminants.ts, 3, 15))
12+
13+
| { str: "b" }
14+
>str : Symbol(str, Decl(missingDiscriminants.ts, 4, 5))
15+
16+
| { num: 1 }
17+
>num : Symbol(num, Decl(missingDiscriminants.ts, 5, 5))
18+
19+
const thing1: Thing = { str: "a", num: 0 }
20+
>thing1 : Symbol(thing1, Decl(missingDiscriminants.ts, 7, 5))
21+
>Thing : Symbol(Thing, Decl(missingDiscriminants.ts, 0, 0))
22+
>str : Symbol(str, Decl(missingDiscriminants.ts, 7, 23))
23+
>num : Symbol(num, Decl(missingDiscriminants.ts, 7, 33))
24+
25+
const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error
26+
>thing2 : Symbol(thing2, Decl(missingDiscriminants.ts, 8, 5))
27+
>Thing : Symbol(Thing, Decl(missingDiscriminants.ts, 0, 0))
28+
>str : Symbol(str, Decl(missingDiscriminants.ts, 8, 23))
29+
>num : Symbol(num, Decl(missingDiscriminants.ts, 8, 33))
30+
31+
const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error
32+
>thing3 : Symbol(thing3, Decl(missingDiscriminants.ts, 9, 5))
33+
>Thing : Symbol(Thing, Decl(missingDiscriminants.ts, 0, 0))
34+
>num : Symbol(num, Decl(missingDiscriminants.ts, 9, 23))
35+
>str : Symbol(str, Decl(missingDiscriminants.ts, 9, 31))
36+
37+
type Item =
38+
>Item : Symbol(Item, Decl(missingDiscriminants.ts, 9, 42))
39+
40+
| { kind: "a", subkind: 0, value: string }
41+
>kind : Symbol(kind, Decl(missingDiscriminants.ts, 12, 5))
42+
>subkind : Symbol(subkind, Decl(missingDiscriminants.ts, 12, 16))
43+
>value : Symbol(value, Decl(missingDiscriminants.ts, 12, 28))
44+
45+
| { kind: "a", subkind: 1, value: number }
46+
>kind : Symbol(kind, Decl(missingDiscriminants.ts, 13, 5))
47+
>subkind : Symbol(subkind, Decl(missingDiscriminants.ts, 13, 16))
48+
>value : Symbol(value, Decl(missingDiscriminants.ts, 13, 28))
49+
50+
| { kind: "b" }
51+
>kind : Symbol(kind, Decl(missingDiscriminants.ts, 14, 5))
52+
53+
const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a"
54+
>item1 : Symbol(item1, Decl(missingDiscriminants.ts, 16, 5))
55+
>Item : Symbol(Item, Decl(missingDiscriminants.ts, 9, 42))
56+
>subkind : Symbol(subkind, Decl(missingDiscriminants.ts, 16, 21))
57+
>kind : Symbol(kind, Decl(missingDiscriminants.ts, 16, 33))
58+
59+
const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property
60+
>item2 : Symbol(item2, Decl(missingDiscriminants.ts, 17, 5))
61+
>Item : Symbol(Item, Decl(missingDiscriminants.ts, 9, 42))
62+
>kind : Symbol(kind, Decl(missingDiscriminants.ts, 17, 21))
63+
>subkind : Symbol(subkind, Decl(missingDiscriminants.ts, 17, 32))
64+
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//// [tests/cases/compiler/missingDiscriminants.ts] ////
2+
3+
=== missingDiscriminants.ts ===
4+
// https://github.com/microsoft/typescript-go/issues/1020
5+
6+
type Thing =
7+
>Thing : Thing
8+
9+
| { str: "a", num: 0 }
10+
>str : "a"
11+
>num : 0
12+
13+
| { str: "b" }
14+
>str : "b"
15+
16+
| { num: 1 }
17+
>num : 1
18+
19+
const thing1: Thing = { str: "a", num: 0 }
20+
>thing1 : Thing
21+
>{ str: "a", num: 0 } : { str: "a"; num: 0; }
22+
>str : "a"
23+
>"a" : "a"
24+
>num : 0
25+
>0 : 0
26+
27+
const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error
28+
>thing2 : Thing
29+
>{ str: "b", num: 1 } : { str: "b"; num: 1; }
30+
>str : "b"
31+
>"b" : "b"
32+
>num : 1
33+
>1 : 1
34+
35+
const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error
36+
>thing3 : Thing
37+
>{ num: 1, str: "b" } : { num: 1; str: "b"; }
38+
>num : 1
39+
>1 : 1
40+
>str : "b"
41+
>"b" : "b"
42+
43+
type Item =
44+
>Item : Item
45+
46+
| { kind: "a", subkind: 0, value: string }
47+
>kind : "a"
48+
>subkind : 0
49+
>value : string
50+
51+
| { kind: "a", subkind: 1, value: number }
52+
>kind : "a"
53+
>subkind : 1
54+
>value : number
55+
56+
| { kind: "b" }
57+
>kind : "b"
58+
59+
const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a"
60+
>item1 : Item
61+
>{ subkind: 1, kind: "b" } : { subkind: number; kind: "b"; }
62+
>subkind : number
63+
>1 : 1
64+
>kind : "b"
65+
>"b" : "b"
66+
67+
const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property
68+
>item2 : Item
69+
>{ kind: "b", subkind: 1 } : { kind: "b"; subkind: number; }
70+
>kind : "b"
71+
>"b" : "b"
72+
>subkind : number
73+
>1 : 1
74+

testdata/baselines/reference/submodule/compiler/discriminateWithMissingProperty.errors.txt

Lines changed: 0 additions & 22 deletions
This file was deleted.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--- old.discriminateWithMissingProperty.errors.txt
2+
+++ new.discriminateWithMissingProperty.errors.txt
3+
@@= skipped -0, +0 lines =@@
4+
-discriminateWithMissingProperty.ts(12,5): error TS2345: Argument of type '{ mode: "numeric"; data: Uint8Array<ArrayBuffer>; }' is not assignable to parameter of type 'Arg'.
5+
- Types of property 'data' are incompatible.
6+
- Type 'Uint8Array<ArrayBuffer>' is not assignable to type 'number'.
7+
-
8+
-
9+
-==== discriminateWithMissingProperty.ts (1 errors) ====
10+
- type Arg = {
11+
- mode: "numeric",
12+
- data: number,
13+
- } | {
14+
- mode: "alphabetic",
15+
- data: string,
16+
- } | {
17+
- data: string | Uint8Array;
18+
- }
19+
-
20+
- declare function foo(arg: Arg): void;
21+
- foo({ mode: "numeric", data: new Uint8Array([30]) }); // Should error
22+
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23+
-!!! error TS2345: Argument of type '{ mode: "numeric"; data: Uint8Array<ArrayBuffer>; }' is not assignable to parameter of type 'Arg'.
24+
-!!! error TS2345: Types of property 'data' are incompatible.
25+
-!!! error TS2345: Type 'Uint8Array<ArrayBuffer>' is not assignable to type 'number'.
26+
+<no content>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// https://github.com/microsoft/typescript-go/issues/1020
5+
6+
type Thing =
7+
| { str: "a", num: 0 }
8+
| { str: "b" }
9+
| { num: 1 }
10+
11+
const thing1: Thing = { str: "a", num: 0 }
12+
const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error
13+
const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error
14+
15+
type Item =
16+
| { kind: "a", subkind: 0, value: string }
17+
| { kind: "a", subkind: 1, value: number }
18+
| { kind: "b" }
19+
20+
const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a"
21+
const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property

0 commit comments

Comments
 (0)