断言另一个属性后,一个属性的打字稿推断

时间:2020-06-25 19:50:38

标签: typescript

我有一个对象,其中一个属性的类型取决于另一个属性的类型。例如:

// 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枚举的每个成员执行,因此不适当。

1 个答案:

答案 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}
})

Playground Link

相关问题