Nammatham Note 2: Convert array of keys and array of values to object
This orignial question is from Stackoverflow. The full description how to construct this type utility (from catchts.com)
type A = ["cat", "dog"];
type B = ["red", "yellow"];
type Expected = {
cat: "red",
dog: "yellow"
}
The original solution
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 Keys = string | number | symbol;
type AllowedKeys<T> = T extends readonly Keys[] ? T : never;
type Mapper<T, U> = T extends Keys ? U extends Keys ? Record<T, U> : never : never;
type Zip<
T extends ReadonlyArray<Keys>,
U extends ReadonlyArray<Keys>,
Result extends Record<string, any> = {}> =
CompareLength<T, U> extends true
? T extends []
? Result :
T extends [infer HeadT1]
? U extends [infer HeadU1]
? Result & Mapper<HeadT1, HeadU1> : never :
T extends [infer HeadT2, ...infer TailT2]
? U extends [infer HeadU2, ...infer TailU2]
? Zip<AllowedKeys<TailT2>, AllowedKeys<TailU2>, Result & Mapper<HeadT2, HeadU2>>
: never
: never
: never;
type Zipper<T extends ReadonlyArray<Keys>, U extends ReadonlyArray<Keys>> =
CompareLength<T, U> extends true ? Zip<T, U> : never;
type A = ["cat", "dog"];
type B = ["red", "yellow"];
type Result = Zipper<A, B>;
const zip: Result = {
cat: "red",
dog: "yellow"
}
playground
I want the Type support value any type, not only string | number | symbol
as already defined in:
type Keys = string | number | symbol;
type AllowedKeys<T> = T extends readonly Keys[] ? T : never;
My Solution
Playground
To modify the original code, I've revise
type Keys = string | number | symbol;
type Mapper<T, U> = T extends Keys
? U extends Keys
? Record<T, U>
: never
: never;
โดยตัว Mapper
ต้องการ value ของที่จะมา Map (U
) เป็น Keys
เท่านั้น ซึ่งก็คือ string | number | symbol
อย่างใดอย่างหนึ่ง ซึ่งผมต้องการให้ Mapper
รับ value ที่เป็นอะไรก็ได้
ดังนั้นจึงเหลือแค่เช็คว่า Key ที่รับเข้ามาเป็น Valid key หรือไม่ ซึ่งก็คือ string | number | symbol
type Mapper<T, U> = T extends Keys ? Record<T, U> : never;
จุดที่ 2 ที่แก้ไขก็คือ การนิยาย type Zip
type Keys = string | number | symbol;
type Zip<
T extends ReadonlyArray<Keys>,
U extends ReadonlyArray<Keys>,
Result extends Record<string, any> = {}
> =
โดยที่ Type รับ parameter 2 ตัวที่เป็น Readonly Array ที่เป็น Key
ทั้ง key และ value เลยจึงทำให้ในการใช้งาน Zip
Zip<["cat", "dog"], ["red", "yellow"]>;
จึงแก้ไขให้รับ any
type แทน
Final Solution
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 Keys = string | number | symbol;
type Mapper<T, U> = T extends Keys ? Record<T, U> : never;
type AllowedKeys<T> = T extends readonly Keys[] ? T : never;
type Zip<
T extends ReadonlyArray<Keys>,
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 & Mapper<HeadT1, HeadU1>
: never
: T extends [infer HeadT2, ...infer TailT2]
? U extends [infer HeadU2, ...infer TailU2]
? Zip<AllowedKeys<TailT2>, TailU2, Result & Mapper<HeadT2, HeadU2>>
: never
: never
: never;
type Zipper<T extends ReadonlyArray<Keys>, U extends ReadonlyArray<any>> = CompareLength<T, U> extends true
? Zip<T, U>
: never;
class Red {}
class Yellow {}
type A = ["cat", "dog"];
type B = [Red, Yellow];
type Result = Zipper<A, B>;
const zip: Result = {
cat: Red,
dog: Yellow
}