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