Home > front end >  Typescript mapping array of different types
Typescript mapping array of different types

Time:05-21

how to avoid union type for each resulting element (needed [valueOfTypeOne, ValueOfTypeTwo] instead of (valueOfTypeOne | ValueOfTypeTwo)[]) in such scenario ?

const [valueOfTypeOne, ValueOfTypeTwo] = await Promise.all(
  [
    fetchTypeOne(),
    fetchTypeTwo(),
  ].map((promise) => promise.catch(() => null)),
);

CodePudding user response:

Using .map() on a tuple will essentially convert the tuple to an array type and you will lose the type information. See here https://github.com/microsoft/TypeScript/issues/6574

One way to fix this is two append the catch to each Promise.

const [valueOfTypeOne, ValueOfTypeTwo] = await Promise.all(
    [
      fetchTypeOne().catch(() => null),
      fetchTypeTwo().catch(() => null),
    ],
);

Playground

CodePudding user response:

A nice utility function that will do the mapping for us and hide away the type of map can do the trick:

function catchToNull<P extends Promise<any>[]>(...promises: P): { [K in keyof P]: Promise<Awaited<P[K]> | null> } {
    return promises.map((p) => p.catch(() => null)) as never;
}

It iterates through each promise and unwraps it, wrapping it back with null.

You would use it like this:

const [valueOfTypeOne, valueOfTypeTwo] = await Promise.all(
    catchToNull(
        fetchTypeOne(),
        fetchTypeTwo(),
    )
);

And in the case of an array of promises, use spread:

catchToNull(...arrayOfPromises);

You might find that you need to type the array of promises as a tuple if necessary:

catchToNull<[Promise<number>]>(...onlyOnePromiseInThisArray);

You can find and try this solution here.

  • Related