I ran into this strange phenomenon in typescript. When using the spread operator, the type checking seems to be lost. I would expect this to throw an error:
interface Input {
thingOne: string;
thingTwo: number;
thingThree: string;
thingFour: boolean;
thingFive: Array<number>
}
interface Output {
thingThree: string;
thingFour: boolean;
thingFive: Array<number>
}
const transformFunction = (input: Input): Output => {
const result: Output = {
...input,
thingFour: !input.thingFour
}
return result
}
I am typing result
as Output
, but spreading an object of type Input
into it would clearly produce a result that is not of the type Output
. Why is typescript not catching this?
What we are expecting here is to trigger Excess Property Check feature of TypeScript. This feature tells us that TypeScript would warn us if there are extra properties in the object if we explicitly mention the object type. Unfortunately, it works only for object literals.
For example this:
const transformFunction = (input: Input): Output => {
return {
thingOne: "string;",
thingTwo: 123,
thingThree: "string;",
thingFour: !input.thingFour,
thingFive: [1, 23],
};
};
Warns:
Type '{ thingOne: string; thingTwo: number; thingThree: string; thingFour: boolean; thingFive: number[]; }' is not assignable to type 'Output'.
Object literal may only specify known properties, and 'thingOne' does not exist in type 'Output'.
It seems like spread operator case is not covered under exces property check. See here: https://github.com/microsoft/TypeScript/issues/41237
This is the excess property check. According to @RyanCavanaugh it's more of a linter, not really part of the type system. But not every case is covered by this check, as you noticed.
They also mentioned looking into ongoing conversion on exact types here: https://github.com/microsoft/TypeScript/issues/12936
The function is type correct since type Input
can be assigned to type Output
without issues, even though it has extra properties. For literal objects only, there is an excess property check, because excess properties in a object literal are very likely to be mistakes. Consider this constant for example.
const o: {a: number} = {a: 1, b: 2}
As o
now has type {a: number}
, there is no typed way to access the b
property, so TypeScript considers it an error.
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