TypeScript: How to make a conditional type that returns an array based on the condition?

Viewed 608

I am trying to type a function that should return an array type, if the input parameter is array type, and a normal parameter otherwise. Here's my attempt:

function test<T extends number|number[]>(a: T):T extends number[] ? string[] : string {
  if (Array.isArray(a)) {
    return ['123', '45']
  }
  return '123'
}

I get an error at the return statements: Type 'string' is not assignable to type 'T extends number[] ? string[] : string'.

What I'm looking for is the following:

const a = test(3) // "a" is string type
const b = test([1,2,3]) // "b" is string[] type
3 Answers

Just use overload your function:

function test(a: number): '123'
function test(a: number[]): string[]
function test<T extends number | number[]>(a: T) {
    if (Array.isArray(a)) {
        return ['123', '45']
    }
    return '123'
}
test(2) // '123'
test([23]) // string[]

No need in conditional types.

Playground

Based on this Typescript will not infer different return types based on type guards in the function. But you can define multiple function signatures like this:

function test(a: number): string;
function test(a: number[]): string[];

function test<T extends number|number[]>(a: T): string | string[]{ 
  if (Array.isArray(a)) {
    return ['123', '45'] 
  }
  let str = '123'
  return str;
}

const a = test(3) //string
const b = test([1,2,3]) //string[]

PlaygroundLink

Also see this issue: https://github.com/microsoft/TypeScript/issues/24929

Union based return types are currently not supported by typescript but there is a workaround for this kind of problem.

function test<T extends number|number[]>(a: T):T extends number[] ? string[] : string {
  if (Array.isArray(a)) {
    return ['123', '45'] as any
  }
  return '123' as any
}

See a working example here.

For more details read this issue on github

Related