首页 > 解决方案 > 类型保护和扩展联合类型?

问题描述

我有一个对象数组,其中每个项目要么扩展,{x: method}要么{y: method}我想尝试将它们分开。我写了以下

export const refresh = <
  N extends HasRefresh | HasRefreshWithFoo,
  RefreshWithFoo extends HasRefreshWithFoo,
  RefreshNormal extends HasRefresh
>(
  ns: N[],
  foos: Foo[]
) => {
  const refreshWithFoo: RefreshWithFoo = ns.filter((n): n is RefreshWithFoo  =>
    n.x !== undefined
  ) 
}

但我得到了错误

A type predicate's type must be assignable to its parameter's type.
  Type 'RefreshNormal' is not assignable to type 'N'.

给定一个包含扩展 HasRefreshWithFoo 或扩展 HasRefresh 的项目的对象数组,我如何将数组与联合分开,以便我有一个只有 RefreshNormal 的数组和一个只有 RefreshWithFoo 的数组?

标签: typescripttypesassertextends

解决方案


您可以通过对泛型类型进行另一个抽象来缩小编译器错误。
第二个抽象负责按给定类型过滤数组。

const filterByType = <T>(arr: any[], key: keyof T): T[] =>
    arr.filter(e => (<T>e)[key]) as T[];

遵循Typescript 指南中的 typeguard an instance,我们被迫指定检查转换是否有效完成的键(我们过滤的类型中的唯一键)。

所以,我们现在可以通过RefreshWithFoo

export const refresh = <
  N extends HasRefresh | HasRefreshWithFoo,
  RefreshWithFoo extends HasRefreshWithFoo,
  RefreshNormal extends HasRefresh
>(
    ns: N[], 
    foos: Foo[]
) => {
    const refreshWithFoo: RefreshWithFoo[] = filterByType<RefreshWithFoo>(ns, `x`);
}

没有任何编译器抱怨。
好吧,我使用的是默认的tsconfig,我认为这不会与任何其他编译器配置抱怨。如果是这样,请告诉我。

编辑
在某些情况下,例如类型交叉点,您将需要多个键来检查。您可以简单地通过期望一个 params 键数组来扩展该方法并检查所有这些键,就像这样

const filterByType = <T>(arr: any[], ...keys: (keyof T)[]): T[] =>
    arr.filter(e => keys.every(k => (<T>e)[k])) as T[];

希望能帮助到你。


推荐阅读