[英]How to define type of an object to accept only keys from enum?
我有一个枚举:
enum MyEnum {
One,
Two,
Three
}
然后我有一个React Component方法,该方法应该返回一个对象,其键只能是MyEnum的字符串键(到目前为止,含义是:1、2、3):
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
的数组。
我猜测reduce
的回调函数主体中的res
是myMethod
的期望返回值,因为您正在回调函数中为其设置值。 如果我们通过名称RetType
调用myMethod
的返回值类型,则意味着RetType
的键必须为keyof typeof MyEnum
的键。
您正在使用v.name
作为这些键,并且由于v
是SomeInterface
,所以我猜SomeInterface
的name
属性也应该是keyof typeof MyEnum
。
您push
v
push
到res
的值上,因此您期望res
的属性是SomeInterface
数组。 因此, RetType
类似于Record<keyof typeof MyEnum, SomeInterface[]>
,其中Record<K,V>
是标准库中的类型,表示对象的键为K
且值为V
但是当然,由于someValue
可能是一个空数组,或者没有MyEnum
的键MyEnum
,因此RetType
可能缺少某些属性。 因此,它更像Partial<Record<keyof typeof MyEnum, SomeInterface[]>>
,其中Partial<T>
是标准库中的类型,它使T
的属性成为可选。
现在,我不确定您是否完全了解reduce
工作方式,但是回调需要返回与预期返回值相同类型的东西。 第二个参数是“初始值”,它还必须与预期的返回值具有相同的类型。 因此,我必须更改您的回调,以便它返回res
,并更改初始值,使其也为RetType
。
我还注意到,由于初始值是一个空对象,因此,当您首次尝试push
某些内容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
。 这使编译器的控制流分析能够正常工作,并确保resVName
实际上是SomeInterface[]
而不是SomeInterface[] | undefined
SomeInterface[] | undefined
。 由于TypeScript中的错误/限制 ,因此无法在res[v.name]
因为控制流分析不适用于方括号属性访问符号。 有点烦人,但确保类型安全可能值得。
该代码的类型都很好,并且可能在运行时有效(您需要检查)。 但是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
}
这也行得通,不会在累加器周围传递。 两种方法都可以。
好的,希望这有道理并能为您提供想法。 祝好运!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.