【发布时间】:2021-09-04 17:55:04
【问题描述】:
我有三个相同类型的变量:
const foo = { name: "foo", age: 12, color: "red" } as const;
const bar = { name: "bar", age: 46, color: "blue" } as const;
const baz = { name: "baz", age: 52, color: "green" } as const;
然后我将它们填充到一个数组中,如下所示:
const arr = [foo,bar,baz];
我想做的是将这个数组(基于name 作为唯一标识符)转换为以“名称”为键的字典。至关重要的是,我需要将键保留为字符串文字类型,以便我可以像这样使用这些属性:
const dict = arrayToDict(arr);
// `dict.foo` is a known key of `dict`
console.log(dict.foo.age);
现在让我从好消息开始,使用我的简单示例可以做到这一点。以下功能将起作用:
export function arrayToObjectName<T extends { name: S }, S extends PropertyKey>(
arr: readonly T[]
) {
return arr
.reduce((acc, v) => ({ ...acc, [v.name]: v }), {} as { [V in T as V["name"]]: V });
}
但是,此函数仅在“名称”已被硬编码后才有效。相反,我希望能够构建一个通用函数,该函数将您要使用的属性作为函数的一部分。我尝试的是这样的:
function arrayToObject<S extends PropertyKey>(prop: S) {
return <T extends { [prop]: S }>(
arr: readonly T[]
) => {
return arr
.reduce((acc, v) => ({ ...acc, [prop]: v }), {} as { [V in T as V[S]]: V });
};
不幸的是,这在两个地方失败了。首先第二个函数签名是不允许的(见错误):
类型文字中的计算属性名称必须引用类型为文字类型或“唯一符号”类型的表达式。
最后一行中的V[S] 分配也失败并出现错误:
类型 'V[S]' 不能分配给类型 'string |号码 |符号'
现在很奇怪,即使如果我使用严格类型(我总是这样做),这些类型错误会阻止我进行转换,但从这个函数中推断出的类型确实有效!
有谁知道我如何实现创建保留类型的通用函数的雄心?在下面的操场中,您可以看到这两个函数以及展示结果的类型测试。
我现在想出了一个替代的通用实现,这也“有效”但仍然有一个错误:
export type Narrowable =
string | number | boolean | symbol | object | undefined | void | null | {};
function arrayToObject<
S extends PropertyKey>(prop: S) {
return <N extends Narrowable, T extends Record<keyof T, N> & Record<S, T[S]>>(
arr: readonly T[]
) => {
return arr.reduce(
(acc, v) => ({ ...acc, [prop]: v }), {} as { [V in T as V[S]]: V }
);
};
}
关于 V[S] 的错误与之前的错误相同,但上述类型确保 S 必须是 keyof T 并消除了我遇到的第一个错误。
【问题讨论】:
标签: typescript typescript-typings typescript-generics