reactjstypescript

How to get TypeScript to narrow a generic conditional type based on a generic boolean?


I'm working with TypeScript conditional types and trying to define a function that behaves differently based on whether a multiple flag is true or false. Here's a simplified version of what I'm doing:

type TypeValue<T, M> = M extends true ? T[] : T

type ArgsTest<T, M> = {
  value: TypeValue<T, M>
  multiple: M
}

const test = <T extends { id: number }, M extends boolean>({
  value,
  multiple,
}: ArgsTest<T, M>) => {
  return multiple ? value.map((v) => v.id) : value.id
}

I'm getting a TypeScript error on value.map, saying that value might not be an array..

Is there a way to get TypeScript to narrow value correctly based on the multiple flag? Or do i have to use function overloads?


Solution

  • M extends true doesn't distinguish between true and false. It distinguishes between true (or never) and every other type.

    Problematically for you, boolean extends boolean, but not boolean extends true, so when M is boolean, the runtime value of multiple can be true, but you have constrained value to be a non-array type.

    You can fix this by making ArgsTest a union type directly:

    type ArgsTest<T> = {
      value: T
      multiple: false
    } | {
      value: T[]
      multiple: true
    };
    
    const test = <T extends { id: number }>({
      value,
      multiple,
    }: ArgsTest<T>) => {
      return multiple ? value.map((v) => v.id) : value.id
    }