在下面的代码块中,如何重写函数
keyValueArrayToObject
以使 keyValueObject
的类型为 {a: number; b: string}
,而不是当前的 {[k: string]: any}
类型?
const arrayOfKeyValue = [
{key: 'a', value: 1},
{key: 'b', value: 'z'},
];
const keyValueArrayToObject = <const T extends readonly {key: PropertyKey; value: any}[]>(
arr: T
) => {
return Object.fromEntries(arr.map(o => [o.key, o.value]));
};
const keyValueObject = keyValueArrayToObject(arrayOfKeyValue);
数组的元素类型的union
,并且对于联合的每个成员
U
,您可以索引到该成员的
key
和
value
属性来获取映射属性的键和值:const keyValueArrayToObject = <const T extends readonly { key: PropertyKey; value: any }[]>(
arr: T
) => {
return Object.fromEntries(arr.map(o => [o.key, o.value])) as
{ [U in T[number] as U['key']]: U['value'] };
};
我们需要一个 类型断言,因为 TypeScript 没有足够的强类型
Object.fromEntries()
。现在我们可以针对您的示例进行测试:
const arrayOfKeyValue = [
{ key: 'a', value: 1 },
{ key: 'b', value: 'z' },
] as const; // <-- need this
const keyValueObject = keyValueArrayToObject(arrayOfKeyValue);
/* const keyValueObject: {
a: 1;
b: "z";
} */
看起来不错,尽管与您要求的略有不同。首先,您需要在数组文字上使用
const
断言或类似的东西。否则 TypeScript 会推断出像
Array<{key: string, value: string} | {key: string, value: number}>
这样的宽类型。这种类型并没有错,但它对于您的需求来说不够具体。您需要编译器至少跟踪 key
属性的文字类型。
通过
const
断言,您还可以获得
value
属性的文字类型,因此
keyValueObject
被推断为具有
{a: 1, b: "z"}
类型。如果这太具体了,那么您需要手动加宽它,或者对数组文字类型进行更多控制,例如 [{ key: 'a' as const, value: 1 },{ key: 'b' as const, value: 'z' }]
或更奇怪的
[{key: 'a', value: 1},{key: 'b', value: 'z'}] satisfies ({ key: "xxx" | (string & {}), value: unknown })[]
。但我认为这超出了所提出问题的范围。