代码之家  ›  专栏  ›  技术社区  ›  Eduard

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

  •  0
  • Eduard  · 技术社区  · 8 年前

    我有一个枚举:

    enum MyEnum {
        One,
        Two,
        Three
    }
    

    然后我有一个react组件的方法,它应该返回一个对象,这个对象的键只能是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
    }
    
    1 回复  |  直到 8 年前
        1
  •  1
  •   jcalz    8 年前

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

    • 自从 v SomeInterface ,似乎 someValue 必须是数组 SomeInterface接口 .

    • 我猜是吧 res 在…的身体里 reduce 的回调函数是 myMethod ,因为您正在回调函数中对其设置值。如果我们调用返回值的类型 我的方法 按名字 RetType ,这意味着 重新键入 必须是 keyof typeof MyEnum .

    • 您正在使用 v.name 作为那些钥匙,从那以后 V SomeInterface接口 那么我猜 SomeInterface接口 name 属性也应为 髓鞘类型键 .

    • 你是 push 惯性导航与制导 V 关于 物件 ,因此您希望 物件 成为 SomeInterface接口 . 因此, 重新键入 是不是有点像 Record<keyof typeof MyEnum, SomeInterface[]> ,在哪里 Record<K,V> 是标准库中的一种类型,它表示一个对象,该对象的键位于 K 和价值观 V .

    • 当然,因为 有价值的 可能是空数组,或者元素的数目不如 MyEnum 然后 重新键入 可能缺少某些属性。所以更像 Partial<Record<keyof typeof MyEnum, SomeInterface[]>> ,在哪里 Partial<T> 是标准库中使属性 T 可选。

    • 现在,我不确定你是否完全理解 reduce 有效,但回调需要返回与预期返回值相同的类型。第二个参数是“初始值”,它必须与预期的返回值类型相同。因此,我必须更改您的回调,以便它返回 物件 和初始值,使其太 重新键入 .

    • 我还注意到,由于初始值是空对象,所以第一次尝试 它的一个数组属性上的某些内容,您会发现它是 undefined . 因此,在尝试之前,需要检查并将其设置为空数组 在上面。

    这让我想到了这样的事情:

    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 . 这允许编译器的 control flow analysis 去工作,确保 重新命名 实际上是一个 SomeInterface[] 而不是一个 SomeInterface[] | undefined . 它不起作用 res[v.name] 因为一个 bug/limitation 在typescript中,控制流分析不使用括号属性访问表示法。这有点烦人,但确保类型安全可能是值得的。

    这些代码类型都很好,可能在运行时工作(您需要检查)。但是 减少 看起来不像你想做的。相反,我想说你在寻找更简单的 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
    }
    

    这同样有效,而且不会影响蓄电池的运转。无论哪种方法都可能是好的。


    好吧,希望这有点道理,给你一些想法。祝你好运!

    推荐文章