我有一个对象,其中一个属性的类型取决于另一个属性的类型。例如:
// The item types
enum ItemType { APPLE, SANDWICH }
// Map of a given item type to it's options
type ItemTypeToOptionsMap = {
[ItemType.APPLE]: { ripeness: string }
[ItemType.SANDWICH: { filling: string }
}
// The item type in question
type Item<T extends ItemType>= {
type: T
options: ItemTypeToOptionsMap[T]
}
我在其他地方有一些打字稿,可以遍历某些项目(可以是任何项目),例如Item<any>[]
,
items.forEach(i => {
if (i.type === ItemType.APPLE)
i.options // <-- Typescript doesn't infer "options" as ItemTypeToOptionsMap[ItemType.APPLE]!
})
如上所述,问题是在给定options
的情况下,Typescript不能推断type
的类型。而是将options
保留为{ ripeness: string } | { filling: string }
(来自上面的示例)。
我想念什么吗?
编辑1:
我知道类似以下功能的东西会起作用:
const isAppleType = (
item: Item<any>
): item is Item<ItemType.APPLE> => (
item.type === ItemType.APPLE
)
但这必须对ItemType
枚举的每个成员执行,因此不适当。
答案 0 :(得分:3)
您想要的行为是有区别的联合的行为,其中type
是判别式。您可以通过映射来构造这样的联合类型:
type ItemUnion = {
[K in ItemType]: { type: K, options: ItemTypeToOptionsMap[K] }
}[ItemType]
然后,如果您还希望使用Item<T>
之类的通用类型,则可以通过限制并集来实现:
type Item<T extends ItemType> = ItemUnion & { type: T }
使用此方法,可以通过如下测试判别式来缩小Item<ItemType>
之类的类型:
let items: Item<ItemType>[] = [];
items.forEach(i => {
if (i.type === ItemType.APPLE)
i.options // {ripeness: string}
})