I'm getting an inconsistent behaviour when I inline the return value type of a function and if I define it in a separate type definition.
For example:
interface Foo {
bar: string;
}
type Fooer = () => Foo;
const foo1: Fooer = () => { // It knows `.bar` should be a string
return {
bar: 123,
};
}
// Return type set in a separate type definition
const foo2: Fooer = () => {
return {
foo: 123, // It doesn't complain about additional properties
bar: "zzz",
};
}
// Inline (repeated) return type
const foo3: Fooer = (): Foo => {
return {
foo: 123, // And now it does...
bar: "zzz",
};
}
Try it on the TypeScript Playground
I'd expect foo2
and foo3
to have the same behaviour (personally I expect both to show the same error, or at least to be consistent).
What am I missing here? What's the difference between both approaches?
What's happening here is a result of TypeScript's 'duck typing'. Basically, the fact that the return type in foo2 has a bar
field that is a string means that the function has a signature () => Promise<{foo: number, bar: string}>
, which can be assigned to the foo2
variable of type Fooer
due to duck typing.
However, in foo3, you can think of the type checking as being applied directly on the return type instead of on the entire function. As a result you see the error Object literal may only specify known properties, and 'foo' does not exist in type 'Foo'.
since the type checking is performed on the object literal, which has the additional requirement that it can't specify unknown properties when you give it an explicit type.