来自两个数组的 Typescript 通用对象类型

问题描述 投票:0回答:1

给定两个数组,

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
typescript lodash typescript-generics typescript-types
1个回答
0
投票

要实现这一点,您需要一起使用元组和映射类型:

// 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'] }
© www.soinside.com 2019 - 2024. All rights reserved.