Alec Mev
Alec Mev

Reputation: 4813

Map over array while preserving discriminated union type

I have an array:

const array: (
  | { foo: true; bar: [number, number] }
  | { foo: false; bar: [number, number, number] }
)[] = [
  { foo: true, bar: [1, 2] },
  { foo: false, bar: [3, 4, 5] },
];

And I'd like to multiply all numbers in it by two. I want to do this:

// Don't try to analyze this function, it's correct
export const mapTuple = <T extends unknown[], R>(
  array: T,
  callback: (value: T[number], index: number, array: T) => R,
): { [K in keyof T]: R } =>
  array.map(
    callback as (value: unknown, index: number, array: unknown[]) => R,
  ) as { [K in keyof T]: R };

const result: typeof array = array.map(({ foo, bar }) => ({
  foo,
  bar: mapTuple(bar, (x) => x * 2),
}));

But, understandably, it produces a Type 'boolean' is not assignable to type 'false'.

My current workaround is to use this silly ternary:

const result: typeof array = array.map(({ foo, bar }) =>
  foo
    ? { foo, bar: mapTuple(bar, (x) => x * 2) }
    : { foo, bar: mapTuple(bar, (x) => x * 2) },
);

Is there a cleaner solution?

Upvotes: 1

Views: 109

Answers (1)

Felix Ekl&#246;f
Felix Ekl&#246;f

Reputation: 3740

Maybe not what your looking for since it uses casting but it's one way to do it.

const result: typeof array = array.map(({ foo, bar }) =>
  ({ foo, bar: mapTuple(bar, (x) => x * 2) }) as (typeof array)[number]
);

Upvotes: 1

Related Questions