如何定义对象的类型以仅接受枚举中的键?

时间:2018-07-11 15:39:23

标签: reactjs typescript

我有一个枚举:

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
}

1 个答案:

答案 0 :(得分:1)

因此,似乎这里发生了很多事情。我的观察和猜测:

  • 由于vSomeInterface,因此看来someValue必须是SomeInterface的数组。

  • 我猜想res的回调函数中的reducemyMethod的期望返回值,因为您是在回调函数。如果我们使用名称myMethod来调用RetType的返回值类型,则意味着RetType的键必须为keyof typeof MyEnum

  • 您将v.name用作这些键,并且由于vSomeInterface,所以我猜想SomeInterface的{​​{1}}属性应该也为name

  • 您正在keyof typeof MyEnumpush放入v的值上,因此您希望res的属性是res的数组。因此,SomeInterface类似于RetType,其中Record<K,V>是标准库中的一种类型,表示一个对象,该对象具有Record<keyof typeof MyEnum, SomeInterface[]>中的键和K中的值。 / p>

  • 但是,当然,由于V可能是一个空数组,或者没有像someValue的键那样多的元素,因此MyEnum可能缺少某些属性。因此,它更像RetType,其中Partial<T>是标准库中的一种类型,它使Partial<Record<keyof typeof MyEnum, SomeInterface[]>>中的属性是可选的。

  • 现在,我不确定您是否完全了解reduce的工作方式,但是回调需要返回与预期返回值相同类型的东西。第二个参数是“初始值”,它还必须与预期的返回值具有相同的类型。因此,我必须更改您的回调,以使其返回T,并更改初始值,使其也为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 } 的新值中进行复制。这使编译器的control flow analysis可以正常工作,并确保resVName实际上是resVName而不是SomeInterface[]。由于TypeScript中有bug/limitation,因此它无法在SomeInterface[] | undefined上运行,因此控制流分析不适用于括号属性访问符号。有点烦人,但确保类型安全可能值得。

该代码的类型都很好,并且可能在运行时有效(您需要检查)。但是res[v.name]似乎与您要执行的操作不同。相反,我想说的是您正在寻找更简单的forEach方法,例如:

reduce

这也可行,不会在累加器周围传递。两种方法都可以,


好的,希望这能给您带来一些想法并给您一些想法。祝你好运!