Acid Coder
Acid Coder

Reputation: 2747

typescript generate index signature type from 2 union types

given

type key = "a" | "b" | "c"
type value = 1 | 2 | 3

I want to generate {a:1, b:2, c:3} using two union types in above

type A<K,V> = ?????

what is ?????

Upvotes: 1

Views: 195

Answers (1)

Technically, it is possible to do. COnsider this example:

type key = "a" | "b" | "c"
type value = 1 | 2 | 3

// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
  k: infer I
) => void
  ? I
  : never;

// credits goes to https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type UnionToOvlds<U> = UnionToIntersection<
  U extends any ? (f: U) => void : never
>;

type PopUnion<U> = UnionToOvlds<U> extends (a: infer A) => void ? A : never;

// credits goes to https://stackoverflow.com/questions/53953814/typescript-check-if-a-type-is-a-union#comment-94748994
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;

type UnionToArray<T, A extends unknown[] = []> = IsUnion<T> extends true
  ? UnionToArray<Exclude<T, PopUnion<T>>, [PopUnion<T>, ...A]>
  : [T, ...A];

type Length<T extends ReadonlyArray<any>> = T extends { length: infer L }
  ? L
  : never;

type CompareLength<
  X extends ReadonlyArray<any>,
  Y extends ReadonlyArray<any>
  > = Length<X> extends Length<Y> ? true : false;


type Callback<T, U> = T extends PropertyKey ? U extends PropertyKey ? Record<T, U> : never : never

// https://catchts.com/tuples#zip 
type Zip<
  T extends ReadonlyArray<any>,
  U extends ReadonlyArray<any>,
  Result extends Record<string, any> = {}> =
  (CompareLength<T, U> extends true
    ? (T extends []
      ? Result
      : (T extends [infer HeadT1]
        ? (U extends [infer HeadU1]
          ? Result & Callback<HeadT1, HeadU1>
          : never)
        : (T extends [infer HeadT2, ...infer TailT2]
          ? (U extends [infer HeadU2, ...infer TailU2]
            ? Zip<TailT2, TailU2, Result & Callback<HeadT2, HeadU2>>
            : never)
          : never
        )
      )
    )
    : never);



type Keys = UnionToArray<key>;

type Values = UnionToArray<value>

// Record<"a", 1> & Record<"b", 2> & Record<"c", 3>
type Result = Zip<Keys, Values>

Playground

I have converted both key and value to tuples and then just merged them.

However, you should not do this in your production code. It is UNSAFE. Please see this issue/17944 and my question.

You should never rely on union order as well as Object.keys order.

Upvotes: 1

Related Questions