Skip to content

Commit 1c7628e

Browse files
committed
Improve discriminated union error messages
Assignability errors for discriminated unions now check the value of the discriminant to decide which member of the union to check for assignability. Previously, assignability didn't know about discriminated unions and would check every member, issuing errors for the last member of the union if assignability failed. For example: ```ts type Square = { kind: "sq", size: number } type Rectangle = { kind: "rt", x: number, y: number } type Circle = { kind: "cr", radius: number } type Shape = | Square | Rectangle | Circle; let shape: Shape = { kind: "sq", x: 12, y: 13, } ``` `typeRelatedToSomeType` now checks whether each property in the source type is a discriminant. It finds `kind` and proceeds to look for the type in the target union that has `kind: "sq"`. If it finds it, which it does in this example (`Square`), then it checks only assignbility to `Square`. The result is that the error now says that property 'size' is missing in type `{ kind: "sq", x: number, y: number }` instead of saying that that "sq" is not assignable to type "cr" like it did before. Fixes microsoft#10867
1 parent 2fc634f commit 1c7628e

File tree

4 files changed

+78
-0
lines changed

4 files changed

+78
-0
lines changed

src/compiler/checker.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7771,6 +7771,11 @@ namespace ts {
77717771
if (target.flags & TypeFlags.Union && containsType(targetTypes, source)) {
77727772
return Ternary.True;
77737773
}
7774+
const discriminantType = findMatchingDiscriminantType(source, target);
7775+
if (discriminantType) {
7776+
return isRelatedTo(source, discriminantType, reportErrors);
7777+
}
7778+
77747779
const len = targetTypes.length;
77757780
for (let i = 0; i < len; i++) {
77767781
const related = isRelatedTo(source, targetTypes[i], reportErrors && i === len - 1);
@@ -7781,6 +7786,23 @@ namespace ts {
77817786
return Ternary.False;
77827787
}
77837788

7789+
function findMatchingDiscriminantType(source: Type, target: UnionOrIntersectionType) {
7790+
const sourceProperties = getPropertiesOfObjectType(source);
7791+
if (sourceProperties) {
7792+
for (const sourceProperty of sourceProperties) {
7793+
if (isDiscriminantProperty(target, sourceProperty.name)) {
7794+
const sourceType = getTypeOfSymbol(sourceProperty);
7795+
for (const type of target.types) {
7796+
const targetType = getTypeOfPropertyOfType(type, sourceProperty.name);
7797+
if (targetType && isRelatedTo(sourceType, targetType)) {
7798+
return type;
7799+
}
7800+
}
7801+
}
7802+
}
7803+
}
7804+
}
7805+
77847806
function typeRelatedToEachType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary {
77857807
let result = Ternary.True;
77867808
const targetTypes = target.types;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
tests/cases/compiler/discriminatedUnionErrorMessage.ts(8,5): error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'.
2+
Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Square'.
3+
Property 'size' is missing in type '{ kind: "sq"; x: number; y: number; }'.
4+
5+
6+
==== tests/cases/compiler/discriminatedUnionErrorMessage.ts (1 errors) ====
7+
type Square = { kind: "sq", size: number }
8+
type Rectangle = { kind: "rt", x: number, y: number }
9+
type Circle = { kind: "cr", radius: number }
10+
type Shape =
11+
| Square
12+
| Rectangle
13+
| Circle;
14+
let shape: Shape = {
15+
~~~~~
16+
!!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'.
17+
!!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Square'.
18+
!!! error TS2322: Property 'size' is missing in type '{ kind: "sq"; x: number; y: number; }'.
19+
kind: "sq",
20+
x: 12,
21+
y: 13,
22+
}
23+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//// [discriminatedUnionErrorMessage.ts]
2+
type Square = { kind: "sq", size: number }
3+
type Rectangle = { kind: "rt", x: number, y: number }
4+
type Circle = { kind: "cr", radius: number }
5+
type Shape =
6+
| Square
7+
| Rectangle
8+
| Circle;
9+
let shape: Shape = {
10+
kind: "sq",
11+
x: 12,
12+
y: 13,
13+
}
14+
15+
16+
//// [discriminatedUnionErrorMessage.js]
17+
var shape = {
18+
kind: "sq",
19+
x: 12,
20+
y: 13
21+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
type Square = { kind: "sq", size: number }
2+
type Rectangle = { kind: "rt", x: number, y: number }
3+
type Circle = { kind: "cr", radius: number }
4+
type Shape =
5+
| Square
6+
| Rectangle
7+
| Circle;
8+
let shape: Shape = {
9+
kind: "sq",
10+
x: 12,
11+
y: 13,
12+
}

0 commit comments

Comments
 (0)