【问题标题】:Typescript: map union type to a single type打字稿:将联合类型映射到单一类型
【发布时间】:2021-01-26 06:33:47
【问题描述】:

我正在尝试缩小(通过推断)我想要从此数组过滤器中删除的类型,但它给了我一个 TypeError: 'Item' is missing the following properties

type ItemList = (Item | ItemGroup )[];
type ItemGroup = {
  name: string;
  items: Item[];
}
type Item = {
  key: string;
  label: string;
}

const list: ItemList = [
   {
      key: 'key',
      label: 'label'
   },
   {
      name: 'name',
      items: [
        {
          key: 'key1',
          label: 'label2'
        },
        {
          key: 'key3',
          label: 'label4'
        },
      ]
   }
]

const groups: ItemGroup[] = list.filter( l => 'name' in l )
      ^^^^^^
// Type '(Item | ItemGroup)[]' is not assignable to type 'ItemGroup[]'.
//   Type 'Item | ItemGroup' is not assignable to type 'ItemGroup'.
//     Type 'Item' is missing the following properties from type 'ItemGroup': name, items ts(2322)

有什么想法吗?

【问题讨论】:

  • 看起来groups 应该输入为ItemList,因为filter 返回一个数组。错误消息实际上告诉您问题所在:您的ItemGroup 没有name
  • 对不起,我在写问题时错过了输入,它应该是ItemGroup[](现在更新)

标签: typescript types typescript-typings union-types


【解决方案1】:

您可以使用type assertion 断言过滤后的结果数组的类型为ItemGroup[]

const groups: ItemGroup[] = list.filter( l => 'name' in l ) as ItemGroup[]

【讨论】:

    【解决方案2】:

    不幸的是,编译器不够聪明,无法查看l => "name" in l 回调并了解它可用于将Item | ItemGroup 缩小为ItemGroup。幸运的是,您可以通过将其注释为user-defined type guard function告诉编译器这是意图:

    const isItemGroup = (l: Item | ItemGroup): l is ItemGroup => "name" in l;
    

    现在如果你调用isItemGroup(l),结果是true,编译器会理解l是一个ItemGroup。此外,标准库提供了一个call signature for Array.prototype.filter(),它接受用户定义的类型保护回调并生成一个缩小的数组。所以通过使用isItemGroup 作为回调,你会得到你想要的结果:

    const groups: ItemGroup[] = list.filter(isItemGroup); // no error now
    

    Playground link to code

    【讨论】:

    • 你比我早了 38 秒发帖!
    • @LindaPaiste 有一瞬间,我以为我已经发布了两次相同的答案,因为它们非常相似。 ?
    • 啊,好吧,这是有道理的。与此同时,我使用了一个 lodash reduce 函数,只检查下一个值是否包含“名称”属性。不过我喜欢这个功能,谢谢!
    【解决方案3】:

    您有一个包含ItemItemGroup 元素的数组。您想将此数组过滤为仅是 ItemGroup 的元素,并且您希望 typescript 了解您已过滤列表并知道返回的类型是 ItemGroup[]

    如何实现这一点是购买将过滤器l => 'name' in l 转换为自己的类型保护功能。返回类型 value is ItemGroup 告诉打字稿“当且仅当这是真的,值是 ItemGroup 类型”。

    const isItemGroup = (value: Item | ItemGroup): value is ItemGroup => 'name' in value;
    
    const groups: ItemGroup[] = list.filter( isItemGroup );
    

    通过使用类型保护,typescript 可以理解list.filter 的含义并且你的错误消失了。

    Playground Link

    【讨论】:

      猜你喜欢
      • 2020-06-07
      • 2019-12-11
      • 1970-01-01
      • 2019-11-19
      • 2022-08-17
      • 2019-01-12
      • 2021-04-18
      • 2022-12-07
      • 1970-01-01
      相关资源
      最近更新 更多