在Typescript中区分联合类型的通用匹配函数

8 generics typescript typescript2.1

是否可以定义类似于区分联合类型的通用匹配函数?假设我们有以下类型定义:

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
}
Run Code Online (Sandbox Code Playgroud)

使用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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

它完成了这项工作,但是定义这样的函数是非常繁琐的,特别是当一个人有很多工会成员时.

是否有可能以某种方式简化此定义,可能使用Mapped Types或来自最新2.1分支的其他现有方法.

我正在玩"Mapped Types",但我不确定即使我知道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);
  }
}
Run Code Online (Sandbox Code Playgroud)

但实际上可以将P转换为相应的Value类型.

Ret*_*sam 1

其关键是能够根据其类型获得工会成员。这可以通过这样的类型来完成:

type UnionMemberByKind<K> = Extract<Union, { kind: K }>
Run Code Online (Sandbox Code Playgroud)

Extract<T, U>T返回该匹配的成员U:在这种情况下,它将返回具有指定的联合的单个成员kind

使用该类型,您可以正确构建匹配器对象:

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

然后基本上像以前一样定义你的匹配函数:

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

as any演员阵容很不幸,但我找不到避免的方法)