如何在Typescript中按属性过滤对象类型的并集?

马特亚·彼得罗维奇(Mateja Petrovic)

想象以下简化的设置:

import { Action, AnyAction } from 'redux'; // interface Action<Type> { type: Type } and type AnyAction = Action<any>

export type FilterActionByType<
  A extends AnyAction,
  ActionType extends string
> = A['type'] extends ActionType ? A : never;

type ActionUnion = Action<'count/get'> | Action<'count/set'>;

type CountGetAction = FilterActionByType<ActionUnion, 'count/get'>;
// expected: Action<'count/get'>
// received: never

有没有办法做到这一点?(打字稿3.7是一个选项)

贾卡尔兹

你想FilterActionByType<A, T>采取联合类型A,并分头行事每个成员,然后把所有的结果,并在一个新的联盟团结他们......你想办法FilterActionByType<A, T>分布在工会(至少A)。通过确保条件类型的形式,您可以使用分布式条件类型来执行此操作type FilterActionByType<A, T> = A extends ...将裸露A作为选中的类型会触发所需的分布。

但是:您的条件类型的形式为type FilterActionByType<A, T> = A["type"] extends ...A由属性查找“覆盖”,因此不是分布式的。这意味着,A["type"] extends ActionType将采用的整个联合值A,即(在您的情况下)ActionUnion并且ActionUnion["type"] extends "count/get"变为("count/get" | "count/set") extends "count/get",这是错误的。X extends (X | Y)永远是正确的,但(X | Y) extends X事实并非如此一般。)所以,你得到never


将您拥有的内容更改为分布式条件类型的最简单方法是将定义包装在A extends any ? ... : never

export type FilterActionByType<
  A extends AnyAction,
  ActionType extends string
> = A extends any ? A['type'] extends ActionType ? A : never : never;

type ActionUnion = Action<'count/get'> | Action<'count/set'>;

type CountGetAction = FilterActionByType<ActionUnion, 'count/get'>;
// type CountGetAction = Action<"count/get">

或者,您可以将原始条件类型重构为可分布的,而无需将其包装:

export type FilterActionByType<
  A extends AnyAction,
  ActionType extends string
> = A extends { type: ActionType } ? A : never;

type CountGetAction = FilterActionByType<ActionUnion, 'count/get'>;
// type CountGetAction = Action<"count/get">

该支票A extends {type: ActionType}是的发行版本A["type"] extends ActionType

两种方法都可以为您工作,但是后者可能更干净。


好吧,希望能有所帮助;祝好运!

链接到代码

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章