给定两个数组,
keyArr
和valArr
,我如何定义一个Typescript泛型来推断对象类型<O>
,其中键来自keyArr
,值来自valArr
(如本answer或在 lodash _.zipObject).
function objectBuilder<
K extends string[],
V extends any[],
O extends object = ???
>(keyArr: K, valArr: V): O {
const result: O = {};
keyArr.forEach((key, i) => result[key] = valArr[i]);
return result;
}
objectBuilder(['foo', 'bar', 'baz'], [11, 22, 33]);
// Type O = { foo: number; bar: number; baz: number; };
对于混合
valArr
,以下任一类型推理都是可以接受的:
objectBuilder(['foo', 'bar', 'baz'], [11, "22", false]);
// Preferred:
// Type O = { foo: number; bar: string; baz: boolean; };
// Also acceptable:
// Type O = {
// foo: number | string | boolean;
// bar: number | string | boolean;
// baz: number | string | boolean;
// };
另外,我们是否可以定义类型使得
keyArr
和valArr
必须具有相同的长度并且valArr
必须由唯一的键组成?
objectBuilder(['foo', 'bar', 'baz'], [11, 22, 33]); // OK
objectBuilder(['foo', 'bar', 'baz'], [11, 22]); // Error
objectBuilder(['foo', 'bar'], [11, 22, 33]); // Error
objectBuilder(['foo', 'bar', 'baz'], [11, 11, 11]); // OK
objectBuilder(['foo', 'bar', 'foo'], [11, 22, 33]); // Error
要实现这一点,您需要一起使用元组和映射类型:
// Creates tuples of corresponding elements from K and V arrays
type Zip<K extends readonly string[], V extends readonly any[]> = {
[I in keyof K]: I extends keyof V ? [K[I], V[I]] : never
};
// Converts a tuple array to an object type
type ObjectType<T extends readonly [string, any][]> = {
[K in T[number][0]]: Extract<T[number], [K, any]>[1]
};
function objectBuilder<
K extends readonly string[],
V extends readonly any[]
>(keyArr: K & { length: V['length'] }, valArr: V): ObjectType<Zip<K, V>> {
const result = {} as any;
keyArr.forEach((key, i) => {
result[key] = valArr[i];
});
return result;
}
// To convert array to a tuple we use "as const",
// tuples preserve their length,
// which is required to force both tuples to be the same length
// { foo: number; bar: number; baz: number; }
const obj1 = objectBuilder(['foo', 'bar', 'baz'] as const, [11, 22, 33] as const);
// { foo: number; bar: string; baz: boolean; }
const obj2 = objectBuilder(['foo', 'bar', 'baz'] as const, [11, "22", false] as const);
要将
keyArr
约束为与 valArr
长度相同的字符串数组,我们可以使用以下类型(K
是 keyArr
的类型,V
是 valArr
的类型:
keyArr: K & { length: V['length'] }