redux 如何在Typescript中通过属性过滤对象类型的联合?

6ioyuze2  于 2023-05-07  发布在  TypeScript
关注(0)|答案(3)|浏览(154)

想象一下以下简化的设置:

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

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

l3zydbqr

l3zydbqr1#

您希望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的整个联合值,即(在您的情况下)ActionUnionActionUnion["type"] extends "count/get"变成("count/get" | "count/set") extends "count/get",这是假的。(X extends (X | Y)总是为真,但(X | Y) extends X一般不为真。)所以你得到never
将你的定义转换为分布式条件类型的最简单的方法是将你的定义 Package 在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">

或者你可以重构你原来的条件类型,使其成为分布式的,而不 Package 它:

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的分布式版本。
无论哪种方式都应该为您工作,但后者可能更干净。
好吧,希望能帮上忙;祝你好运!
链接到代码

ffdz8vbo

ffdz8vbo2#

Extract<T, U>对此非常有用,并且比条件类型简单得多。对于下面的示例,您可以将其视为“过滤器”:

// cat meows but doesn't bark
type Cat = { meow: true, bark: false };

// dog and coyote bark
type Coyote = { bark: true };
type Dog = { bark: true };

// all the animals
type Animal = Cat | Dog | Coyote;

// Filter / Extract Animal types that are assignable to { bark: true }
type Barkers = Extract<Animal, { bark: true }>;

// Result: 
// type Barkers == Dog | Coyote

可能不太可能对OP问题有用,但也有一些很酷的功能用于过滤Map类型(来自TS 4.1)。

2w3rbyxf

2w3rbyxf3#

Piotr Lewandowski的最佳解决方案:

type FilterFlags<Base, Condition> = {
    [Key in keyof Base]: 
        Base[Key] extends Condition ? Key : never
};
type AllowedNames<Base, Condition> = 
        FilterFlags<Base, Condition>[keyof Base];
type SubType<Base, Condition> = 
        Pick<Base, AllowedNames<Base, Condition>>;

相关问题