Home > Net >  How to type return value which is optionally an array (depends on input)
How to type return value which is optionally an array (depends on input)

Time:01-17

I don't know how to type following return value properly to clear typescript error.

function simplifiedFn(
  ids: string | string[],
): typeof ids extends string[] ? number[] : number {
  const idsIsArray = Array.isArray(ids);
  const idsProvided = idsIsArray ? ids : [ids];
  
  const results = idsProvided.map((id) => parseInt(id));

  return idsIsArray ? results : results[0];
}

Getting error:

Type 'number | number[]' is not assignable to type 'number'. Type 'number[]' is not assignable to type 'number'.

playground

Many thanks for any hint :)

EDIT (more context):

Please note that this is simplified version of real function, therefore duplicating parseInt(id) is not an option, it would be duplication of bigger complexity, also don't want to move that complexity into separate method as it would need many parameters to be passed back and forward which leads to another complexity in current method.

IMHO typescript should not force you to change your best implementation just for explaining TS the situation... that's not good principle.

I think there is an possibility to explain better Array.isArray to TS, but I didn't succeed. Also I have tried alternative ids instanceof Array but dd not helped.

CodePudding user response:

TypeScript inference is not strong enough to infer the conditional return type is correct. You can use generics so that the conditional you've provided is correctly evaluated based on the type of ids, but inside the function you need to tell the type-checker you've faithfully returned the correct type:

function simplifiedFn<T extends string | string[]>(
  ids: T,
): T extends string[] ? number[] : number {
  const idsIsArray = Array.isArray(ids);
  const idsProvided = idsIsArray ? ids : [ids];
  
  const results = idsProvided.map((id) => parseInt(id));

  return idsIsArray ? results : results[0] as T extends string[] ? number[] : number;
}

simplifiedFn('0') // number
simplifiedFn(['0', '1']) // number[]

CodePudding user response:

You can overload your function like this:

function simplifiedFn(ids: string): number;
function simplifiedFn(ids: string[]): number[];

function simplifiedFn(
  ids: string | string[],
) {
  const idsIsArray = Array.isArray(ids);
  const idsProvided = idsIsArray ? ids : [ids];
  
  const results = idsProvided.map((id) => parseInt(id));

  return idsIsArray ? results : results[0];
}

const result = simplifiedFn('string');
//    ^? const result: number
const resultArray = simplifiedFn(['string']);
//    ^? const resultArray: number[]
  • Related