Skip to content

Commit 9f6a04c

Browse files
authored
Fix translation error that disabled union discrimination optimization (#1757)
1 parent 3d123c9 commit 9f6a04c

File tree

6 files changed

+314
-17
lines changed

6 files changed

+314
-17
lines changed

internal/checker/checker.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29417,7 +29417,7 @@ func (c *Checker) discriminateContextualTypeByObjectMembers(node *ast.Node, cont
2941729417

2941829418
func (c *Checker) getMatchingUnionConstituentForObjectLiteral(unionType *Type, node *ast.Node) *Type {
2941929419
keyPropertyName := c.getKeyPropertyName(unionType)
29420-
if keyPropertyName == "" {
29420+
if keyPropertyName != "" {
2942129421
propNode := core.Find(node.AsObjectLiteralExpression().Properties.Nodes, func(p *ast.Node) bool {
2942229422
return p.Symbol() != nil && ast.IsPropertyAssignment(p) && p.Symbol().Name == keyPropertyName && c.isPossiblyDiscriminantValue(p.Initializer())
2942329423
})

internal/checker/relater.go

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,24 +1167,22 @@ func (c *Checker) mapTypesByKeyProperty(types []*Type, keyPropertyName string) m
11671167
for _, t := range types {
11681168
if t.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsInstantiableNonPrimitive) != 0 {
11691169
discriminant := c.getTypeOfPropertyOfType(t, keyPropertyName)
1170-
if discriminant != nil {
1171-
if !isLiteralType(discriminant) {
1172-
return nil
1173-
}
1174-
duplicate := false
1175-
for _, d := range discriminant.Distributed() {
1176-
key := c.getRegularTypeOfLiteralType(d)
1177-
if existing := typesByKey[key]; existing == nil {
1178-
typesByKey[key] = t
1179-
} else if existing != c.unknownType {
1180-
typesByKey[key] = c.unknownType
1181-
duplicate = true
1182-
}
1183-
}
1184-
if !duplicate {
1185-
count++
1170+
if discriminant == nil || !isLiteralType(discriminant) {
1171+
return nil
1172+
}
1173+
duplicate := false
1174+
for _, d := range discriminant.Distributed() {
1175+
key := c.getRegularTypeOfLiteralType(d)
1176+
if existing := typesByKey[key]; existing == nil {
1177+
typesByKey[key] = t
1178+
} else if existing != c.unknownType {
1179+
typesByKey[key] = c.unknownType
1180+
duplicate = true
11861181
}
11871182
}
1183+
if !duplicate {
1184+
count++
1185+
}
11881186
}
11891187
}
11901188
if count >= 10 && count*2 >= len(types) {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
missingDiscriminants2.ts(32,23): error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'.
2+
missingDiscriminants2.ts(33,34): error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'.
3+
4+
5+
==== missingDiscriminants2.ts (2 errors) ====
6+
// https://github.com/microsoft/typescript-go/issues/1020
7+
8+
// This tests ensures the change also works for discriminated unions with more than 10 cases
9+
10+
type Thing =
11+
| { str: "a", num: 0 }
12+
| { str: "b" }
13+
| { str: "c" }
14+
| { str: "d" }
15+
| { str: "e" }
16+
| { str: "f" }
17+
| { str: "g" }
18+
| { str: "h" }
19+
| { str: "i" }
20+
| { str: "j" }
21+
| { str: "k" }
22+
| { str: "l" }
23+
| { str: "m" }
24+
| { str: "n" }
25+
| { str: "o" }
26+
| { num: 1 }
27+
28+
const thing1: Thing = { str: "a", num: 0 }
29+
const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error
30+
const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error
31+
32+
type Item =
33+
| { kind: "a", subkind: 0, value: string }
34+
| { kind: "a", subkind: 1, value: number }
35+
| { kind: "b" }
36+
37+
const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a"
38+
~~~~~~~
39+
!!! error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'.
40+
const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property
41+
~~~~~~~
42+
!!! error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'.
43+
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//// [tests/cases/compiler/missingDiscriminants2.ts] ////
2+
3+
=== missingDiscriminants2.ts ===
4+
// https://github.com/microsoft/typescript-go/issues/1020
5+
6+
// This tests ensures the change also works for discriminated unions with more than 10 cases
7+
8+
type Thing =
9+
>Thing : Symbol(Thing, Decl(missingDiscriminants2.ts, 0, 0))
10+
11+
| { str: "a", num: 0 }
12+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 5, 5))
13+
>num : Symbol(num, Decl(missingDiscriminants2.ts, 5, 15))
14+
15+
| { str: "b" }
16+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 6, 5))
17+
18+
| { str: "c" }
19+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 7, 5))
20+
21+
| { str: "d" }
22+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 8, 5))
23+
24+
| { str: "e" }
25+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 9, 5))
26+
27+
| { str: "f" }
28+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 10, 5))
29+
30+
| { str: "g" }
31+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 11, 5))
32+
33+
| { str: "h" }
34+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 12, 5))
35+
36+
| { str: "i" }
37+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 13, 5))
38+
39+
| { str: "j" }
40+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 14, 5))
41+
42+
| { str: "k" }
43+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 15, 5))
44+
45+
| { str: "l" }
46+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 16, 5))
47+
48+
| { str: "m" }
49+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 17, 5))
50+
51+
| { str: "n" }
52+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 18, 5))
53+
54+
| { str: "o" }
55+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 19, 5))
56+
57+
| { num: 1 }
58+
>num : Symbol(num, Decl(missingDiscriminants2.ts, 20, 5))
59+
60+
const thing1: Thing = { str: "a", num: 0 }
61+
>thing1 : Symbol(thing1, Decl(missingDiscriminants2.ts, 22, 5))
62+
>Thing : Symbol(Thing, Decl(missingDiscriminants2.ts, 0, 0))
63+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 22, 23))
64+
>num : Symbol(num, Decl(missingDiscriminants2.ts, 22, 33))
65+
66+
const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error
67+
>thing2 : Symbol(thing2, Decl(missingDiscriminants2.ts, 23, 5))
68+
>Thing : Symbol(Thing, Decl(missingDiscriminants2.ts, 0, 0))
69+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 23, 23))
70+
>num : Symbol(num, Decl(missingDiscriminants2.ts, 23, 33))
71+
72+
const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error
73+
>thing3 : Symbol(thing3, Decl(missingDiscriminants2.ts, 24, 5))
74+
>Thing : Symbol(Thing, Decl(missingDiscriminants2.ts, 0, 0))
75+
>num : Symbol(num, Decl(missingDiscriminants2.ts, 24, 23))
76+
>str : Symbol(str, Decl(missingDiscriminants2.ts, 24, 31))
77+
78+
type Item =
79+
>Item : Symbol(Item, Decl(missingDiscriminants2.ts, 24, 42))
80+
81+
| { kind: "a", subkind: 0, value: string }
82+
>kind : Symbol(kind, Decl(missingDiscriminants2.ts, 27, 5))
83+
>subkind : Symbol(subkind, Decl(missingDiscriminants2.ts, 27, 16))
84+
>value : Symbol(value, Decl(missingDiscriminants2.ts, 27, 28))
85+
86+
| { kind: "a", subkind: 1, value: number }
87+
>kind : Symbol(kind, Decl(missingDiscriminants2.ts, 28, 5))
88+
>subkind : Symbol(subkind, Decl(missingDiscriminants2.ts, 28, 16))
89+
>value : Symbol(value, Decl(missingDiscriminants2.ts, 28, 28))
90+
91+
| { kind: "b" }
92+
>kind : Symbol(kind, Decl(missingDiscriminants2.ts, 29, 5))
93+
94+
const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a"
95+
>item1 : Symbol(item1, Decl(missingDiscriminants2.ts, 31, 5))
96+
>Item : Symbol(Item, Decl(missingDiscriminants2.ts, 24, 42))
97+
>subkind : Symbol(subkind, Decl(missingDiscriminants2.ts, 31, 21))
98+
>kind : Symbol(kind, Decl(missingDiscriminants2.ts, 31, 33))
99+
100+
const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property
101+
>item2 : Symbol(item2, Decl(missingDiscriminants2.ts, 32, 5))
102+
>Item : Symbol(Item, Decl(missingDiscriminants2.ts, 24, 42))
103+
>kind : Symbol(kind, Decl(missingDiscriminants2.ts, 32, 21))
104+
>subkind : Symbol(subkind, Decl(missingDiscriminants2.ts, 32, 32))
105+
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
//// [tests/cases/compiler/missingDiscriminants2.ts] ////
2+
3+
=== missingDiscriminants2.ts ===
4+
// https://github.com/microsoft/typescript-go/issues/1020
5+
6+
// This tests ensures the change also works for discriminated unions with more than 10 cases
7+
8+
type Thing =
9+
>Thing : Thing
10+
11+
| { str: "a", num: 0 }
12+
>str : "a"
13+
>num : 0
14+
15+
| { str: "b" }
16+
>str : "b"
17+
18+
| { str: "c" }
19+
>str : "c"
20+
21+
| { str: "d" }
22+
>str : "d"
23+
24+
| { str: "e" }
25+
>str : "e"
26+
27+
| { str: "f" }
28+
>str : "f"
29+
30+
| { str: "g" }
31+
>str : "g"
32+
33+
| { str: "h" }
34+
>str : "h"
35+
36+
| { str: "i" }
37+
>str : "i"
38+
39+
| { str: "j" }
40+
>str : "j"
41+
42+
| { str: "k" }
43+
>str : "k"
44+
45+
| { str: "l" }
46+
>str : "l"
47+
48+
| { str: "m" }
49+
>str : "m"
50+
51+
| { str: "n" }
52+
>str : "n"
53+
54+
| { str: "o" }
55+
>str : "o"
56+
57+
| { num: 1 }
58+
>num : 1
59+
60+
const thing1: Thing = { str: "a", num: 0 }
61+
>thing1 : Thing
62+
>{ str: "a", num: 0 } : { str: "a"; num: 0; }
63+
>str : "a"
64+
>"a" : "a"
65+
>num : 0
66+
>0 : 0
67+
68+
const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error
69+
>thing2 : Thing
70+
>{ str: "b", num: 1 } : { str: "b"; num: 1; }
71+
>str : "b"
72+
>"b" : "b"
73+
>num : 1
74+
>1 : 1
75+
76+
const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error
77+
>thing3 : Thing
78+
>{ num: 1, str: "b" } : { num: 1; str: "b"; }
79+
>num : 1
80+
>1 : 1
81+
>str : "b"
82+
>"b" : "b"
83+
84+
type Item =
85+
>Item : Item
86+
87+
| { kind: "a", subkind: 0, value: string }
88+
>kind : "a"
89+
>subkind : 0
90+
>value : string
91+
92+
| { kind: "a", subkind: 1, value: number }
93+
>kind : "a"
94+
>subkind : 1
95+
>value : number
96+
97+
| { kind: "b" }
98+
>kind : "b"
99+
100+
const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a"
101+
>item1 : Item
102+
>{ subkind: 1, kind: "b" } : { subkind: number; kind: "b"; }
103+
>subkind : number
104+
>1 : 1
105+
>kind : "b"
106+
>"b" : "b"
107+
108+
const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property
109+
>item2 : Item
110+
>{ kind: "b", subkind: 1 } : { kind: "b"; subkind: number; }
111+
>kind : "b"
112+
>"b" : "b"
113+
>subkind : number
114+
>1 : 1
115+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// https://github.com/microsoft/typescript-go/issues/1020
5+
6+
// This tests ensures the change also works for discriminated unions with more than 10 cases
7+
8+
type Thing =
9+
| { str: "a", num: 0 }
10+
| { str: "b" }
11+
| { str: "c" }
12+
| { str: "d" }
13+
| { str: "e" }
14+
| { str: "f" }
15+
| { str: "g" }
16+
| { str: "h" }
17+
| { str: "i" }
18+
| { str: "j" }
19+
| { str: "k" }
20+
| { str: "l" }
21+
| { str: "m" }
22+
| { str: "n" }
23+
| { str: "o" }
24+
| { num: 1 }
25+
26+
const thing1: Thing = { str: "a", num: 0 }
27+
const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error
28+
const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error
29+
30+
type Item =
31+
| { kind: "a", subkind: 0, value: string }
32+
| { kind: "a", subkind: 1, value: number }
33+
| { kind: "b" }
34+
35+
const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a"
36+
const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property

0 commit comments

Comments
 (0)