簡體   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