Home > Back-end >  Typescript - exact return type of function
Typescript - exact return type of function

Time:05-05

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:

https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgGIHt3IN4FgBQyRycA-AFzIDOYUoA5gQL4EFgCeADihuqiMgC8yABQBKIQD40mVvgToQNZDExDRYyryk4CxZFAhgArlAF5C 4nEoByOLYA0eq0QBGdt0 QB6H8gB5AGkXIhZ8cIIFJTAVTAAmLUx dXEdC31DEzNdS1cbZHsnUKsPQq9HX38AdQALdmRgKmQQdFjoKHQoEgAbKixa6AhSEZLwpiA

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.

  • Related