打字稿:将联合体类型映射为单一类型

时间:2020-10-11 22:11:15

标签: typescript types typescript-typings union-types

我正在尝试从数组过滤器中缩小(推论)我想要的类型,但这给了我TypeError:'Item'缺少以下属性

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)

有什么想法吗?

3 个答案:

答案 0 :(得分:1)

不幸的是,编译器不够聪明,无法查看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,则编译器将了解lItemGroup。此外,标准库提供了一个call signature for Array.prototype.filter(),它接受​​用户定义的类型保护回调并产生一个缩小的数组。因此,通过使用isItemGroup作为回调,您将获得所需的结果:

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

Playground link to code

答案 1 :(得分:1)

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

您可以通过将过滤器l => 'name' in l变成其自己的类型保护功能来实现。返回类型value is ItemGroup告诉打字稿“如果且仅当为true时,值的类型为ItemGroup”。

const isItemGroup = (value: Item | ItemGroup): value is ItemGroup => 'name' in value;

const groups: ItemGroup[] = list.filter( isItemGroup );

通过使用类型保护,打字稿可以理解list.filter的含义,并且错误会消失。

Playground Link

答案 2 :(得分:0)

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

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