Home > front end >  Typescript: function return type is different when a parameter is undefined or its type is asserted
Typescript: function return type is different when a parameter is undefined or its type is asserted

Time:01-26

The need

The code is simple but my use case is complex:

type MyFn = <B extends ['A']>(
  param?: B
) => B extends undefined ? 'TYPE_1' : 'TYPE_2'

// so that
const test1 = myFn() // shall be type TYPE_1
const test2 = myFn(['A']) // shall be type TYPE_2
  • I would like that if param is undefined, then the type is 'TYPE_1' else it is 'TYPE_2'
  • paramshall be dynamically typed, the goal is that the IDE autocompletes/validates values when writing the values inside the array
  • the type of param inside the function shall be at least (string[] | undefined)

What I have tried

I tried a lot of different configurations without success: typescript playground

The closest I could get actually was with this code:

type MyFn = <
  T,
  B extends ['A']
>(
  param?: T | B
) => T extends string[] ? 'TYPE_2' : 'TYPE_1'

var a: MyFn3 = (param?) => (param ? 'TYPE_1' : 'TYPE_2') as any

const test1 = a() // IS type TYPE_1
const test2  = a(['A']) // IS be type TYPE_2

But the problem is that the inferred type inside of the function is any because T has no constraint type. But at the moment we constraint the T type, the condition in the return type returns always true.

Is there any solutions to get around that problem ?


Real world use case

The main goal behind such a type is to define an array of string as props address of an object, and return the value at that address.
Eg: ['level1', 'level2'] would return myObject.level1.level2

Here is a simplified version of this real world use case

CodePudding user response:

Your B type is never undefined, because ['A'] is never undefined. The compiler can effectively simplify your expression:

type MyFn = <B extends ['A']>(
  param?: B
) => B extends undefined ? 'TYPE_1' : 'TYPE_2'

to

type MyFn = <B extends ['A']>(
  param?: B
) => false ? 'TYPE_1' : 'TYPE_2'

to

type MyFn = <B extends ['A']>(
  param?: B
) => 'TYPE_2'

The fact that param is optionally B has no effect on B itself.

You need to broaden B:

type MyFn = <B extends ['A'] | undefined>(

which produces the correct result for

const test1 = myFn(undefined) // TYPE_1

and then default to make it work when omitted:

type MyFn = <B extends ['A'] | undefined = undefined>(

which produces the correct result for

const test1 = myFn() // TYPE_1
  • Related