reactjs - 如何定义对象的类型以仅接受来自枚举的键?
问题描述
我有一个枚举:
enum MyEnum {
One,
Two,
Three
}
然后我有一个 React Component 方法,它应该返回一个对象,其键只能是 MyEnum 的字符串键(意思是,到目前为止:一、二、三):
myFunction = (someValue: {}[]): {} => {
return someValue.reduce((res, v, i) => {
res[v.name].push(v)
return res
}, {})
}
问题:如何动态定义该函数返回值的类型为对象,其键只能是MyEnum的字符串键?
现在只有 3 个,但是如果我在枚举中添加另一个,myFunction 的返回值的类型也应该更新。
更新1:
尝试分配Record<keyof typeof MyEnum, any>
为返回值:
myMethod = (someValue: {}[]): Record<keyof typeof MyEnum, any> => {
return someValue.reduce((res: any, v: SomeInterface) => {
res[v.name].push(v)
return res
}, {})
}
这不会引发任何错误,但是如果我将 reduce 的返回值分配给一个变量,然后在其上添加另一个属性(其键不是 MyEnum 键),则不会出现错误,这不是我所寻求的:
myMethod = (someValue: {}[]): Record<keyof typeof MyEnum, any> => {
let someVar = someValue.reduce((res: any, v: SomeInterface) => {
res[v.name].push(v)
return res
}, {})
someVar.keyName = 'value'
return someVar
}
解决方案
所以,看起来这里发生了很多事情。我的观察和猜测:
既然
v
是SomeInterface
,看来someValue
一定是一个数组SomeInterface
。我猜想
res
在回调函数的主体中reduce
是所需的返回值myMethod
,因为你在回调函数中设置了它的值。myMethod
如果我们用名字来调用返回值的类型RetType
,那就意味着RetType
必须是的键keyof typeof MyEnum
。您正在使用
v.name
as 那些键,因为v
isSomeInterface
,那么我猜SomeInterface
's 的name
属性也应该是keyof typeof MyEnum
.您正在考虑
push
的v
值res
,因此您希望 的属性res
是 的数组SomeInterface
。因此,RetType
类似于Record<keyof typeof MyEnum, SomeInterface[]>
,其中Record<K,V>
是标准库中的一种类型,表示具有 in 中的键K
和值的对象V
。但是,当然,由于
someValue
可能是一个空数组,或者没有与 的键一样多的元素MyEnum
,那么RetType
可能会丢失一些属性。所以它更像是标准库Partial<Record<keyof typeof MyEnum, SomeInterface[]>>
中Partial<T>
的一个类型,它使属性成为T
可选的。现在,我不确定您是否完全理解其工作方式
reduce
,但回调需要返回与预期返回值相同类型的内容。第二个参数是“初始值”,它也必须是与预期返回值相同的类型。因此,我必须更改您的回调,使其返回res
,并更改初始值,使其太RetType
。我还注意到,由于初始值是一个空对象,所以当你第一次尝试将
push
某些东西放到它的一个数组属性上时,你会发现它是undefined
. 因此,您需要检查它并将其设置为一个空数组,然后再尝试使用push
它。
这让我想到了这样的事情:
enum MyEnum {
One,
Two,
Three
}
interface SomeInterface {
name: keyof typeof MyEnum
};
type RetType = Partial<Record<keyof typeof MyEnum, SomeInterface[]>>
const myMethod = (someValue: SomeInterface[]): RetType => {
let someVar = someValue.reduce((res: RetType, v: SomeInterface) => {
const resVName = res[v.name] || []; // make sure it's set
resVName.push(v);
res[v.name]=resVName; // copy back in case it's a new array
return res;
}, {} as RetType)
//someVar.keyName = 'value' // errors now
return someVar
}
请注意,我复制到一个名为resVName
. 这允许编译器的控制流分析工作,并确保它resVName
实际上是 aSomeInterface[]
而不是 a SomeInterface[] | undefined
。res[v.name]
由于 TypeScript 中的错误/限制,控制流分析不适用于括号属性访问表示法,因此它不起作用。这有点烦人,但确保类型安全可能是值得的。
该代码的类型都很好,并且可能在运行时工作(您需要检查)。但reduce
似乎不像你想要做的。相反,我会说您正在寻找更简单的forEach
方法,如下所示:
const myMethod = (someValue: SomeInterface[]): RetType => {
let someVar: RetType = {};
someValue.forEach((v: SomeInterface) => {
const someVarVName = someVar[v.name] || []; // make sure it's set
someVarVName.push(v);
someVar[v.name] = someVarVName; // copy back in case it's a new array
});
//someVar.keyName = 'value'
return someVar
}
这也有效,并且不会在累加器周围传递。无论哪种方式都很好,可能。
好的,希望这有点道理,并给你一些想法。祝你好运!
推荐阅读
- r - SelectInput 选项包括列标题
- solr - 如何在 Alfresco Solr 搜索中查询不区分大小写?
- c# - 防止应用程序在 dll 中出现未处理的异常时崩溃
- python - 为什么我的代码的一部分与另一个工作部分相同不起作用
- sprite-kit - iOS 中的 SpriteKit 纹理映射
- scala - 匹配 Scala Manifest
- bash - SED 电子邮件替换(sql 转储中的 json 值)
- c# - 测试该属性是否具有子验证器 FluentValidation
- python - Simpy:在分配新资源时同时释放资源
- c# - 使用 DrawArc (C# OOP) 绘制 trainrail-bends