0

Not a clue how to ask this well, so here is what I have and what I'm after:

Start:

type Initial = {
    foo: string | number
}

End:

type Final = {
    foo: string
} | {
    foo: number
}

I need a type using generics that I can put Initial into that will output Final.

Edit: (Mapping through multiple union values) Playground Link

Edit 2 Thanks to the answer by Titian Cernicova-Dragomir I now have type that can distribute union values in an object into a union of objects, with no unions.

However:

The issue I'm now facing is, if I have multiple union values in an object:

type Initial = {
    foo: string | number,
    bar: "bar" | "anotherStr"
    otherProp: string
}

type FinalNew = {
    foo: string;
    bar: "bar"
    otherProp: string;
} | {
    foo: number;
    bar: "bar"
    otherProp: string;
} | {
    foo: string;
    bar: "anotherStr"
    otherProp: string;
} | {
    foo: number;
    bar: "anotherStr"
    otherProp: string;
}

I can only change 1 union value in to a union of objects at a time:

type DistributedFoo = DistributeUnion<Initial, "foo">

type FinalNew = DistributeUnion<DistributedFoo, "bar">

I cannot do both in the same pass:

type FinalNew = DistributeUnion<Initial, "foo" | "bar">

The appended question then is:

  • Is it possible to do this without having to assign the output type of DistributeUnion to a new type for each key:value you need to distribute?
2
  • Will Initial have other properties ? Or just the one ? Commented Feb 1, 2022 at 16:27
  • it'll have others, also potentially unions Commented Feb 1, 2022 at 16:28

1 Answer 1

3

You can create such a type, that will take a key and a type and distribute over the type of that property:

type Initial = {
    foo: string | number,
    otherProp: string
}
type Id<T> = {} & { [P in keyof T]: T[P]}

type DistributeUnion<T, TKey extends keyof T> = 
  T[TKey] extends infer U  // Make a type parameter that contains T[TKey]
    ? U extends U // We then distribute over the union
      ? Id<Record<TKey, U> & Omit<T, TKey>> // Create a new type with just one union constituent from T[TKey], and the rest of the props in T
      : never
    : never;
type Final = DistributeUnion<Initial, 'foo'>

// type Final = {
//     foo: string;
//     otherProp: string;
// } | {
//     foo: number;
//     otherProp: string;
// }

Playground Link

Sign up to request clarification or add additional context in comments.

6 Comments

great answer, a lot to unpack from it. To clarify I'm understanding this U extends U is turning string | number into string then number? I'm now attempting to iterate over a union of keys to distribute
@MasterNoob exactly. Distributive conditional types are applied to each constituent member of a union and then the result unioned typescriptlang.org/docs/handbook/2/…
I've been trying to allow distributing 2 unions in an object like this type Final = DistributeUnion<Initial, 'foo' | 'bar'> but cant workout how to get the output of distributing foo in to bar, do you know of a way to do this?
I have made an edit to a playground link, containing the approach I've been taking, feels like I'm going about it the wrong way tho
I have made a more in depth edit to the question, hope this helps
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.