Consider this code:
let foo: {a: any} | {b: any} = {a: 1, b: 2};
I expect TypeScript to reject this code, since this union type means either the first type or the second type, and that the value is not assignable to either of them, therefore the type checking should reject it. But in fact it passes, why?
(For background, the aforementioned type is a intended to be an "either this property or that property but not both, type". See Typescript Interface - Possible to make "one or the other" properties required?)
According to the blog post introducing union excess property checking in TypeScript 3.5, TypeScript will check that it belongs to at least one type:
In TypeScript 3.4 and earlier, certain excess properties were allowed in situations where they really shouldn’t have been. For instance, TypeScript 3.4 permitted the incorrect name property in the object literal even though its types don’t match between Point and Label.
type Point = { x: number; y: number; }; type Label = { name: string; }; const thing: Point | Label = { x: 0, y: 0, name: true // uh-oh! };
Previously, a non-disciminated union wouldn’t have any excess property checking done on its members, and as a result, the incorrectly typed name property slipped by.
In TypeScript 3.5, the type-checker at least verifies that all the provided properties belong to some union member and have the appropriate type, meaning that the sample above correctly issues an error.
The blog post goes on to explicitly describe overlap as being valid:
Note that partial overlap is still permitted as long as the property types are valid.
const pl: Point | Label = { x: 0, y: 0, name: "origin" // okay };
Based on the phrase "all the provided properties belong to some union member and have the appropriate type", it appears that the excess property checker is not able to treat those properties as mutually exclusive.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With