I have a problem with return type of the function. In the example, if I explicitly define return type Foo, it shows error as expected. If I add type to function, an error is not shown.
It seems that in foo2 should be at least object extended from Foo, if I remove "a" from foo2, it says this:
Type '() => { b: string; }' is not assignable to type 'FooFn'. Type '{ b: string; }' has no properties in common with type 'Foo'.
interface Foo {
a?: string
}
type FooFn = () => Foo
const foo = (): Foo => {
return {
a: 'a',
b: 'b', // OK
}
}
const foo2: FooFn = () => {
return {
a: 'a',
b: 'b', // Why is not error also here???
}
}
The only option, that I think of is making it something like that:
interface Foo {
a?: string
}
// I cant figure it out, how to write it ...
type FooFn = () => Record<string, key is from keyof Foo ? string : never>
Is it possible to make type, which only accepts keyof Foo and the rest is never?
Playground:
CodePudding user response:
The behaviour you are observing for const foo2: FooFn = () => {
is a result of TypeScript's Structural typing system which implies that as long as the returned value of foo2
has all the properties of Foo
type - the function will pass no matter what other properties the resulting object may contain.
The reason why you are seeing an error for const foo = (): Foo => {
is because you are defining an explicit return type which acts like type annotation, whereas the use of FooFn
kicks in a layer of abstraction that acts like type assertion which does not care about extra properties that are returned and only makes sure that the types have matching values.
This can be illustrated by:
let obj1: { 'a': string } = { a: 'a', b: 'b' } // will not pass for annotation
let obj2 = { a: 'a', b: 'b' } as { 'a': string } // will pass for assertion
Referring back to your example, using type assertion is safe as the returned object from foo2
will still have the type of Foo
, meaning that only property a
can be accessed no matter what else it has during application runtime.
So unfortunately your problem is attempting to break TypeScript's type assertion convention and it may be worth reassessing your approach.
Thankyou to @jcalz for helping to improve this answer.
CodePudding user response:
Use typescript predefined ReturnType
for return type of a function ReturnType<typeof fn>
. As a sidenot there is also a Parameters
for arguments of it.
PS: If you want you can also see how the type works so that you can lear from it.