I have noticed this behaviour in typescript
interface Foo {
x: () => { x: 'hello' };
}
const a: Foo = {
x: () => {
return {
x: 'hello',
excess: 3, // no error
}
},
}
I found some issues in github that state that this is because of function return type widening.
What does function return type widening mean?
Excess property checks don't kick in unless you directly assign a value to a reference of a specified type. So unless you directly assign the object literal to a parameter or variable or return that are explicitly typed you will not get an error.
In this case the x function is typed independently as part of determining the type of the outer object literal and it's return type is determined to be { x: 'hello', excess: 3 }. Then this function is checked for compatibility with the x in Foo and is found to be compatible. At no point was a direct assignment of the object literal to an explicitly typed reference checked. And thus no excess property checks triggered.
The widening that you found in the issues doesn't play into the absence of an error here IMO. The compiler uses contextual types to determine some types. So for example it will use the fact that the outer object literal is assigned to a Foo reference as the context, and this will help in determining parameter types for example, and it will stop the widening of { x: "hello" } to { x: string }, but it will not trigger excess property checks.
Not saying this is the way it should work, and I have seen a lot of questions that revolve around this exact issue, the lack of excess property checks when retuning an object literal from a function that in theory has its return type determined by the context it's declared in.
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