mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-06 04:35:21 -05:00
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 #10867
This commit is contained in:
@@ -7771,6 +7771,11 @@ namespace ts {
|
||||
if (target.flags & TypeFlags.Union && containsType(targetTypes, source)) {
|
||||
return Ternary.True;
|
||||
}
|
||||
const discriminantType = findMatchingDiscriminantType(source, target);
|
||||
if (discriminantType) {
|
||||
return isRelatedTo(source, discriminantType, reportErrors);
|
||||
}
|
||||
|
||||
const len = targetTypes.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const related = isRelatedTo(source, targetTypes[i], reportErrors && i === len - 1);
|
||||
@@ -7781,6 +7786,23 @@ namespace ts {
|
||||
return Ternary.False;
|
||||
}
|
||||
|
||||
function findMatchingDiscriminantType(source: Type, target: UnionOrIntersectionType) {
|
||||
const sourceProperties = getPropertiesOfObjectType(source);
|
||||
if (sourceProperties) {
|
||||
for (const sourceProperty of sourceProperties) {
|
||||
if (isDiscriminantProperty(target, sourceProperty.name)) {
|
||||
const sourceType = getTypeOfSymbol(sourceProperty);
|
||||
for (const type of target.types) {
|
||||
const targetType = getTypeOfPropertyOfType(type, sourceProperty.name);
|
||||
if (targetType && isRelatedTo(sourceType, targetType)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function typeRelatedToEachType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary {
|
||||
let result = Ternary.True;
|
||||
const targetTypes = target.types;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
tests/cases/compiler/discriminatedUnionErrorMessage.ts(8,5): error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'.
|
||||
Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Square'.
|
||||
Property 'size' is missing in type '{ kind: "sq"; x: number; y: number; }'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/discriminatedUnionErrorMessage.ts (1 errors) ====
|
||||
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 = {
|
||||
~~~~~
|
||||
!!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'.
|
||||
!!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Square'.
|
||||
!!! error TS2322: Property 'size' is missing in type '{ kind: "sq"; x: number; y: number; }'.
|
||||
kind: "sq",
|
||||
x: 12,
|
||||
y: 13,
|
||||
}
|
||||
|
||||
21
tests/baselines/reference/discriminatedUnionErrorMessage.js
Normal file
21
tests/baselines/reference/discriminatedUnionErrorMessage.js
Normal file
@@ -0,0 +1,21 @@
|
||||
//// [discriminatedUnionErrorMessage.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,
|
||||
}
|
||||
|
||||
|
||||
//// [discriminatedUnionErrorMessage.js]
|
||||
var shape = {
|
||||
kind: "sq",
|
||||
x: 12,
|
||||
y: 13
|
||||
};
|
||||
12
tests/cases/compiler/discriminatedUnionErrorMessage.ts
Normal file
12
tests/cases/compiler/discriminatedUnionErrorMessage.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
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,
|
||||
}
|
||||
Reference in New Issue
Block a user