护卫和扩展联合类型?

时间:2020-05-06 06:25:07

标签: typescript types assert extends

我有一个对象数组,每个项目都可以扩展{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数组?

1 个答案:

答案 0 :(得分:1)

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

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

Typescript guide of how to type guard 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[];

希望有帮助。