diff --git a/internal/checker/checker.go b/internal/checker/checker.go index b6d46d2e70..4471013ca4 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -29417,7 +29417,7 @@ func (c *Checker) discriminateContextualTypeByObjectMembers(node *ast.Node, cont func (c *Checker) getMatchingUnionConstituentForObjectLiteral(unionType *Type, node *ast.Node) *Type { keyPropertyName := c.getKeyPropertyName(unionType) - if keyPropertyName == "" { + if keyPropertyName != "" { propNode := core.Find(node.AsObjectLiteralExpression().Properties.Nodes, func(p *ast.Node) bool { return p.Symbol() != nil && ast.IsPropertyAssignment(p) && p.Symbol().Name == keyPropertyName && c.isPossiblyDiscriminantValue(p.Initializer()) }) diff --git a/internal/checker/relater.go b/internal/checker/relater.go index e6315974d0..0917a12887 100644 --- a/internal/checker/relater.go +++ b/internal/checker/relater.go @@ -1167,24 +1167,22 @@ func (c *Checker) mapTypesByKeyProperty(types []*Type, keyPropertyName string) m for _, t := range types { if t.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsInstantiableNonPrimitive) != 0 { discriminant := c.getTypeOfPropertyOfType(t, keyPropertyName) - if discriminant != nil { - if !isLiteralType(discriminant) { - return nil - } - duplicate := false - for _, d := range discriminant.Distributed() { - key := c.getRegularTypeOfLiteralType(d) - if existing := typesByKey[key]; existing == nil { - typesByKey[key] = t - } else if existing != c.unknownType { - typesByKey[key] = c.unknownType - duplicate = true - } - } - if !duplicate { - count++ + if discriminant == nil || !isLiteralType(discriminant) { + return nil + } + duplicate := false + for _, d := range discriminant.Distributed() { + key := c.getRegularTypeOfLiteralType(d) + if existing := typesByKey[key]; existing == nil { + typesByKey[key] = t + } else if existing != c.unknownType { + typesByKey[key] = c.unknownType + duplicate = true } } + if !duplicate { + count++ + } } } if count >= 10 && count*2 >= len(types) { diff --git a/testdata/baselines/reference/compiler/missingDiscriminants2.errors.txt b/testdata/baselines/reference/compiler/missingDiscriminants2.errors.txt new file mode 100644 index 0000000000..6d94bf1573 --- /dev/null +++ b/testdata/baselines/reference/compiler/missingDiscriminants2.errors.txt @@ -0,0 +1,43 @@ +missingDiscriminants2.ts(32,23): error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'. +missingDiscriminants2.ts(33,34): error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'. + + +==== missingDiscriminants2.ts (2 errors) ==== + // https://github.com/microsoft/typescript-go/issues/1020 + + // This tests ensures the change also works for discriminated unions with more than 10 cases + + type Thing = + | { str: "a", num: 0 } + | { str: "b" } + | { str: "c" } + | { str: "d" } + | { str: "e" } + | { str: "f" } + | { str: "g" } + | { str: "h" } + | { str: "i" } + | { str: "j" } + | { str: "k" } + | { str: "l" } + | { str: "m" } + | { str: "n" } + | { str: "o" } + | { num: 1 } + + const thing1: Thing = { str: "a", num: 0 } + const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error + const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error + + type Item = + | { kind: "a", subkind: 0, value: string } + | { kind: "a", subkind: 1, value: number } + | { kind: "b" } + + const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a" + ~~~~~~~ +!!! error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'. + const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property + ~~~~~~~ +!!! error TS2353: Object literal may only specify known properties, and 'subkind' does not exist in type '{ kind: "b"; }'. + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/missingDiscriminants2.symbols b/testdata/baselines/reference/compiler/missingDiscriminants2.symbols new file mode 100644 index 0000000000..1c7fd1cb80 --- /dev/null +++ b/testdata/baselines/reference/compiler/missingDiscriminants2.symbols @@ -0,0 +1,105 @@ +//// [tests/cases/compiler/missingDiscriminants2.ts] //// + +=== missingDiscriminants2.ts === +// https://github.com/microsoft/typescript-go/issues/1020 + +// This tests ensures the change also works for discriminated unions with more than 10 cases + +type Thing = +>Thing : Symbol(Thing, Decl(missingDiscriminants2.ts, 0, 0)) + + | { str: "a", num: 0 } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 5, 5)) +>num : Symbol(num, Decl(missingDiscriminants2.ts, 5, 15)) + + | { str: "b" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 6, 5)) + + | { str: "c" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 7, 5)) + + | { str: "d" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 8, 5)) + + | { str: "e" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 9, 5)) + + | { str: "f" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 10, 5)) + + | { str: "g" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 11, 5)) + + | { str: "h" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 12, 5)) + + | { str: "i" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 13, 5)) + + | { str: "j" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 14, 5)) + + | { str: "k" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 15, 5)) + + | { str: "l" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 16, 5)) + + | { str: "m" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 17, 5)) + + | { str: "n" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 18, 5)) + + | { str: "o" } +>str : Symbol(str, Decl(missingDiscriminants2.ts, 19, 5)) + + | { num: 1 } +>num : Symbol(num, Decl(missingDiscriminants2.ts, 20, 5)) + +const thing1: Thing = { str: "a", num: 0 } +>thing1 : Symbol(thing1, Decl(missingDiscriminants2.ts, 22, 5)) +>Thing : Symbol(Thing, Decl(missingDiscriminants2.ts, 0, 0)) +>str : Symbol(str, Decl(missingDiscriminants2.ts, 22, 23)) +>num : Symbol(num, Decl(missingDiscriminants2.ts, 22, 33)) + +const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error +>thing2 : Symbol(thing2, Decl(missingDiscriminants2.ts, 23, 5)) +>Thing : Symbol(Thing, Decl(missingDiscriminants2.ts, 0, 0)) +>str : Symbol(str, Decl(missingDiscriminants2.ts, 23, 23)) +>num : Symbol(num, Decl(missingDiscriminants2.ts, 23, 33)) + +const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error +>thing3 : Symbol(thing3, Decl(missingDiscriminants2.ts, 24, 5)) +>Thing : Symbol(Thing, Decl(missingDiscriminants2.ts, 0, 0)) +>num : Symbol(num, Decl(missingDiscriminants2.ts, 24, 23)) +>str : Symbol(str, Decl(missingDiscriminants2.ts, 24, 31)) + +type Item = +>Item : Symbol(Item, Decl(missingDiscriminants2.ts, 24, 42)) + + | { kind: "a", subkind: 0, value: string } +>kind : Symbol(kind, Decl(missingDiscriminants2.ts, 27, 5)) +>subkind : Symbol(subkind, Decl(missingDiscriminants2.ts, 27, 16)) +>value : Symbol(value, Decl(missingDiscriminants2.ts, 27, 28)) + + | { kind: "a", subkind: 1, value: number } +>kind : Symbol(kind, Decl(missingDiscriminants2.ts, 28, 5)) +>subkind : Symbol(subkind, Decl(missingDiscriminants2.ts, 28, 16)) +>value : Symbol(value, Decl(missingDiscriminants2.ts, 28, 28)) + + | { kind: "b" } +>kind : Symbol(kind, Decl(missingDiscriminants2.ts, 29, 5)) + +const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a" +>item1 : Symbol(item1, Decl(missingDiscriminants2.ts, 31, 5)) +>Item : Symbol(Item, Decl(missingDiscriminants2.ts, 24, 42)) +>subkind : Symbol(subkind, Decl(missingDiscriminants2.ts, 31, 21)) +>kind : Symbol(kind, Decl(missingDiscriminants2.ts, 31, 33)) + +const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property +>item2 : Symbol(item2, Decl(missingDiscriminants2.ts, 32, 5)) +>Item : Symbol(Item, Decl(missingDiscriminants2.ts, 24, 42)) +>kind : Symbol(kind, Decl(missingDiscriminants2.ts, 32, 21)) +>subkind : Symbol(subkind, Decl(missingDiscriminants2.ts, 32, 32)) + diff --git a/testdata/baselines/reference/compiler/missingDiscriminants2.types b/testdata/baselines/reference/compiler/missingDiscriminants2.types new file mode 100644 index 0000000000..6782ddb5d1 --- /dev/null +++ b/testdata/baselines/reference/compiler/missingDiscriminants2.types @@ -0,0 +1,115 @@ +//// [tests/cases/compiler/missingDiscriminants2.ts] //// + +=== missingDiscriminants2.ts === +// https://github.com/microsoft/typescript-go/issues/1020 + +// This tests ensures the change also works for discriminated unions with more than 10 cases + +type Thing = +>Thing : Thing + + | { str: "a", num: 0 } +>str : "a" +>num : 0 + + | { str: "b" } +>str : "b" + + | { str: "c" } +>str : "c" + + | { str: "d" } +>str : "d" + + | { str: "e" } +>str : "e" + + | { str: "f" } +>str : "f" + + | { str: "g" } +>str : "g" + + | { str: "h" } +>str : "h" + + | { str: "i" } +>str : "i" + + | { str: "j" } +>str : "j" + + | { str: "k" } +>str : "k" + + | { str: "l" } +>str : "l" + + | { str: "m" } +>str : "m" + + | { str: "n" } +>str : "n" + + | { str: "o" } +>str : "o" + + | { num: 1 } +>num : 1 + +const thing1: Thing = { str: "a", num: 0 } +>thing1 : Thing +>{ str: "a", num: 0 } : { str: "a"; num: 0; } +>str : "a" +>"a" : "a" +>num : 0 +>0 : 0 + +const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error +>thing2 : Thing +>{ str: "b", num: 1 } : { str: "b"; num: 1; } +>str : "b" +>"b" : "b" +>num : 1 +>1 : 1 + +const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error +>thing3 : Thing +>{ num: 1, str: "b" } : { num: 1; str: "b"; } +>num : 1 +>1 : 1 +>str : "b" +>"b" : "b" + +type Item = +>Item : Item + + | { kind: "a", subkind: 0, value: string } +>kind : "a" +>subkind : 0 +>value : string + + | { kind: "a", subkind: 1, value: number } +>kind : "a" +>subkind : 1 +>value : number + + | { kind: "b" } +>kind : "b" + +const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a" +>item1 : Item +>{ subkind: 1, kind: "b" } : { subkind: number; kind: "b"; } +>subkind : number +>1 : 1 +>kind : "b" +>"b" : "b" + +const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property +>item2 : Item +>{ kind: "b", subkind: 1 } : { kind: "b"; subkind: number; } +>kind : "b" +>"b" : "b" +>subkind : number +>1 : 1 + diff --git a/testdata/tests/cases/compiler/missingDiscriminants2.ts b/testdata/tests/cases/compiler/missingDiscriminants2.ts new file mode 100644 index 0000000000..7da014321d --- /dev/null +++ b/testdata/tests/cases/compiler/missingDiscriminants2.ts @@ -0,0 +1,36 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/typescript-go/issues/1020 + +// This tests ensures the change also works for discriminated unions with more than 10 cases + +type Thing = + | { str: "a", num: 0 } + | { str: "b" } + | { str: "c" } + | { str: "d" } + | { str: "e" } + | { str: "f" } + | { str: "g" } + | { str: "h" } + | { str: "i" } + | { str: "j" } + | { str: "k" } + | { str: "l" } + | { str: "m" } + | { str: "n" } + | { str: "o" } + | { num: 1 } + +const thing1: Thing = { str: "a", num: 0 } +const thing2: Thing = { str: "b", num: 1 } // Shouldn't be error +const thing3: Thing = { num: 1, str: "b" } // Shouldn't be error + +type Item = + | { kind: "a", subkind: 0, value: string } + | { kind: "a", subkind: 1, value: number } + | { kind: "b" } + +const item1: Item = { subkind: 1, kind: "b" } // Error, type "b" not assignable to type "a" +const item2: Item = { kind: "b", subkind: 1 } // Error, 'subkind' isn't a known property