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

Posted

技术标签:

【中文标题】Typescript中可区分联合类型的通用匹配函数【英文标题】:Generic match function for discriminated union types in Typescript 【发布时间】:2017-04-10 22:35:38 【问题描述】:

是否可以在可区分的联合类型上定义诸如通用匹配函数之类的东西? 假设我们有以下类型定义:

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

使用 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);
    
  

它可以完成这项工作,但是定义这样的函数非常繁琐,尤其是当一个有很多工会成员时。

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

我在玩“映射类型”,但我不确定是否真的有可能获得具体的Value,即使我知道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);
  

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

【问题讨论】:

【参考方案1】:

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

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

Extract&lt;T, U&gt; 返回与 U 匹配的 T 的成员:在这种情况下,它将返回具有指定 kind 的联合的单个成员。

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

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

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

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

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

【讨论】:

以上是关于Typescript中可区分联合类型的通用匹配函数的主要内容,如果未能解决你的问题,请参考以下文章

如何使用泛型缩小 TypeScript 联合类型

扩展排他联合的打字稿通用参数

使用联合类型进行类型推断 - 不存在最佳通用类型

是否有一个通用函数来识别嵌套的区分联合的情况?

差异联合类型和区分联合打字稿/ F#

TypeScript 中的函数重载与使用联合类型