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
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.
CodePudding user response:
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)