繁体   English   中英

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

[英]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
}

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

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

  • 我猜测reduce的回调函数主体中的resmyMethod的期望返回值,因为您正在回调函数中为其设置值。 如果我们通过名称RetType调用myMethod的返回值类型,则意味着RetType的键必须为keyof typeof MyEnum的键。

  • 您正在使用v.name作为这些键,并且由于vSomeInterface ,所以我猜SomeInterfacename属性也应该是keyof typeof MyEnum

  • push v pushres的值上,因此您期望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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM