1

With a union of array types (NOT an array of unions, e.g. Foo[] | Bar[], not (Foo|Bar)[]), is it possible to call something like Array.prototype.map on the union to the effect of safely mapping each union variant to a new value with the SAME TYPE (e.g. same variant, but perhaps changing some fields, etc).

Here is an example:

type HomogenousLetters = 'a'[] | 'b'[];
const array: HomogenousLetters = ['a', 'a', 'a'] as HomogenousLetters;

Now, suppose we wanted to map between this array and one of the same union variant. We might want to call Array.prototype.map, but this fails to compile in even a trivial case.

// ERROR: Type '("a" | "b")[]' is not assignable to type '"a"[] | "b"[]'.
const mappedArray: HomogenousLetters = array.map((letter) => letter);

I understand this is reasonable behaviour - the type signature of Array.prototype.map() gives no guarantee that homogenous inputs give homogenous outputs (i.e. that each "letter" in array will be of the same union variant), but in that case, how COULD we ensure this?

David Wood
  • 123
  • 8
  • What version of typescript? This works for me https://tsplay.dev/wjJG7w – smac89 Nov 01 '21 at 15:15
  • Does this answer your question? [How to use array.map with tuples in typescript?](https://stackoverflow.com/questions/57913193/how-to-use-array-map-with-tuples-in-typescript) – captain-yossarian from Ukraine Nov 01 '21 at 16:14
  • @smac89 I think in your example the compiler is narrowing the union to the literal type of ['a', 'a', 'a']. See this playground link for a counter example: https://tsplay.dev/wgLJyN – David Wood Nov 01 '21 at 16:33
  • @captain-yossarian No, I don't think so. My problem is narrowing unions rather than constraining properties like Array.length, and I'm not sure how to generalise any of the suggestions there. – David Wood Nov 01 '21 at 16:38
  • I think I've shown that what you are attempting to do in your question is possible. You should ask a new question with the new example you have – smac89 Nov 01 '21 at 16:50
  • 1
    Sorry for the confusion, I cast in the initialisation of my union type in my original example specifically to prevent this confusion. – David Wood Nov 01 '21 at 16:52
  • 1
    I see. In short, I don't think it is possible for the compiler to make such a guarantee. Typescript seems to have done a good job of widening the resulting type to `(a|b)[]` because that's the best that can be done in this situation. What you are looking for is runtime type-inference, which although possible to some extent (using type guards), it is just not feasible in this case to make such an assertion – smac89 Nov 01 '21 at 17:14
  • Also note that for this to work, `array.map` has to return an array which is not just generic in the type of the elements, but in the type of the array itself (i.e. a union of arrays)! In short, that flexibility is not built into the current type signature of `array.map`. So you either need to extend `array.map` to also return union array, or you have to create your own function that does this – smac89 Nov 01 '21 at 17:18
  • 1
    Your question talks about doing it "safely", but safety is not your problem, right? The output type `("a" | "b")[]` isn't so much *unsafe* as much as it is *incomplete*. Really you want the compiler to be "smarter", not safer. And it pretty much can't be here, without some sort of [distributive control flow analysis](//github.com/microsoft/TypeScript/issues/25051), and TS doesn't support that. I think nothing you do will be better than `const mappedArray = array.map((letter: T) => letter) as HomogenousLetters;`, where you take the narrowing job away from the relatively inept compiler. – jcalz Nov 01 '21 at 19:39
  • @jcalz I wish I could accept your comment as an answer because in retrospect this was basically the information I was missing - thank you. Feel free to repost as an answer and I will accept so others can find it. – David Wood Nov 01 '21 at 20:00
  • 1
    I could probably write up an answer, but it's underwhelming (e.g., "sorry TS won't do this for you automatically, anything you try will be worse than a type assertion"). Maybe if I get some time later I'll do it – jcalz Nov 01 '21 at 20:03

0 Answers0