简体   繁体   English

Typescript中已识别联合类型的通用匹配函数

[英]Generic match function for discriminated union types in Typescript

Is it possible to define something like a generic match function over discriminated union type? 是否可以在已区分的联合类型上定义类似通用匹配函数的内容? Let's say we have the following type definitions: 假设我们有以下类型定义:

const Kinds = {
  A: 'A',
  B: 'B',
};
type Kind = typeof Kinds.A | typeof Kinds.B;
type Value = A | B;
interface A {
  kind: Kinds.A
}
interface B {
  kind: Kinds.B
}

With switch statement it is possible to define match function like: 使用switch语句可以定义匹配函数,例如:

interface Matcher<T> {
  Kinds.A: (value: A) => T
  Kinds.B: (value: B) => T
}
function match<T>(matcher: Matcher<T>) {
  return function(value: Value) {
    switch (value.kind) {
      case Kinds.A: return matcher[Kinds.A](value);
      case Kinds.B: return matcher[Kinds.B](value);
    }
  }
}

It does the job, but it is very tedious to define such functions especially when one have many union members. 它确实可以完成工作,但是定义这样的功能非常繁琐,尤其是当一个工会成员很多时。

Is it possible to simplify this definition somehow, maybe with Mapped Types or other existing means from the latest 2.1 branch. 是否有可能以某种方式简化此定义,例如使用映射类型或最新2.1分支中的其他现有方式。

I was playing around with "Mapped Types", but I'm not sure that it is actually possible to get concrete Value even when I know Kind , eg something like: 我一直在玩“映射类型”,但是我不确定即使我知道Kind ,实际上也有可能获得具体的价值 ,例如:

type Matcher<T> = {[P in Kind]: (value: P) => T};
function match<T>(matcher: Matcher<T>) {
  return function(value: Value) {
    return matcher[value.kind](value);
  }
}

but where one can actually translate P to corresponding Value type. 但实际上可以将P转换为相应的Value类型。

The key to this is being able to get a member of a union based on its kind. 这样做的关键是能够根据工会的种类来获得工会的成员。 That can be done with a type like this: 可以用这样的类型来完成:

type UnionMemberByKind<K> = Extract<Union, { kind: K }>

Extract<T, U> returns the members of T that match U : in this case it's going to return the single member of the union with the specified kind . Extract<T, U>返回与U匹配的T的成员:在这种情况下,它将返回具有指定kind的并集的单个成员。

With that type, you can properly build your matcher object: 使用该类型,您可以正确构建匹配器对象:

type Matcher<Res> = {
    [P in Union["kind"]]: (value: UnionMemberByKind<P>) => Res
}

And then define your match function basically as before: 然后基本上像以前一样定义您的匹配函数:

function match<T>(matcher: Matcher<T>) {
    return function(value: Union) {
        return matcher[value.kind](value as any);
    }
}

(The as any cast is unfortunate, but I can't find a way to avoid it) (不幸的是, as any演员表,但我找不到避免它的方法)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM