我编写了一个函数,将对象转换为数组,其中数组的每个元素都包含对象的ID。目前,此代码可以很好地与以下代码配合使用:
const objectToArray = <T>(object: { [id: string]: T }): (T & { id: string })[] => {
return Object.keys(object).map((key) => ({ id: key, ...object[key] }))
}
这将转换{ a: { value: 1 }, b: { value: 2 } }
为[{ id: 'a', value: 1 }, { id: 'b', value: 2 }]
。
我想做的是获取第二个参数idField
,该参数是对象字段的名称,用作返回的对象中的ID字段。例如,如果idField
具有该值,userId
则结果将为[{ userId: 'a', value: 1 }, { userId: 'b', value: 2 }]
。
我已经在函数的逻辑中完成了此操作,但是我无法弄清楚返回类型应该是什么:
export const objectToArray = <T>(object: { [id: string]: T }, idField: string): ???[] => {
return Object.keys(object).map((key) => ({ [idField]: key, ...object[key] }))
}
我尝试将return类型设置为,(T & { [idField]: string })[]
但出现错误A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.
。
这有点复杂,因为值(函数参数)是运行时,而类型只是编译时。没有什么可以阻止您将非常量字符串传递给的idField
,而完全放弃了您在此处要求的类型检查。
但是,如果您确实向函数传递了常量字符串,则可以获得所需的类型安全性。
让我们从一个基本示例开始(在javascript中-省略的类型)-
function foo(prop) {
return {
[prop]: 42
};
}
只是一个函数,它带有属性名称,并使用它来返回具有该属性的对象。本质上,这就是您在示例中显示的内容吧?您将如何输入?
function foo<T extends string>(prop: T): Record<T, number> {
return {
[prop]: 42
} as Record<T, number>;
}
在操场上尝试
这是如何运作的?我们首先确保prop
是一个扩展字符串的类型(也可以是通用的T extends string | number | symbol
),因为这是您需要prop
用作属性的类型。
然后,我们使用此类型创建返回类型Record<T, number>
。这与-
{
[k in T]: number
}
您可以使用/或。
但是,该断言是必需的。由于打字稿无法始终将参数强制为编译时间常数。
这将使用在编译时已知的字符串正确键入check-
let s = foo('userName');
// ^ Record<'userName', number>
s.userName; // all good
s.otherProp; // Property 'otherProp' does not exist on type 'Record<"userName", number>'
好的,现在让我们将其用于您的功能-
export const objectToArray = <T, K extends string>(
object: { [id: string]: T },
idField: K
): (T & Record<K, string>)[] => {
return Object.keys(object).map(key => ({ [idField]: key, ...object[key] })) as (T & Record<K, string>)[];
};
请注意,我再次假设K
(的类型idField
)将是一个字符串-您可以使用,也可以根据使用情况将K extends string | number | symbol
其任意组合。我还假设带有的属性的值K
是string
,因此Record<K, string>
在操场上尝试
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句