Function argument type inference and conditional type

I’m trying to cover following case:

I have a function which accepts a boolean and returns value of RealItem | ImaginaryItem type. I’m using conditional type to narrow the return type based on boolean argument.

type RealItem = { color: string }
type ImaginaryItem = { description: string }

export function createItem<T extends boolean>(isReal: T): T extends true ? RealItem : ImaginaryItem

export function createItem<T extends boolean>(isReal: T): RealItem | ImaginaryItem {
  return isReal ? { color: 'red' } : { description: 'Crazy thing' }

My signatures seem to be working fine for following cases:

const a = createItem(true)      | typeof a -> RealItem
const b = createItem(false)     | typeof b -> ImaginaryItem

let isReal = true
const a = createItem(isReal)    | typeof a -> RealItem

let isReal = false
const b = createItem(isReal)    | typeof b -> ImaginaryItem

However if I wrap createItem into another function and pass isReal argument:

const itemsFactory = (isReal: boolean) => createItem(isReal)

TS seems to fail to deduct return type narrowed with conditional return type:

const a = itemsFactory(true)    | typeof a -> RealItem | ImaginaryItem
const b = itemsFactory(false)   | typeof b -> RealItem | ImaginaryItem

TS playground

I’m not gonna lie, the union type makes me very unhappy.

  • Is this some sort of limitation of TS capabilities in type interference?
  • Can I help TS understand how to interpret higher function arguments?

I know I could use type guards here, however they don’t go well with my current code structure.

>Solution :

The type information of isReal is essentially lost with the itemsFactory function since you specified isReal as a boolean here.

To fix this, you can also add the same generic type to the itemsFactory function:

const itemsFactory = <T extends boolean>(isReal: T) => createItem(isReal)


Leave a Reply