可以强打吗?

时间:2019-01-21 17:48:05

标签: typescript types discriminated-union

我正在尝试使用Typescript对区分联合的支持,在Typescript中实现一种伪模式匹配,同时利用match函数和代表match表达式分支的对象。

这是我想使用的场景:

type Shape = 
    | { kind: 'Circle', radius: number }
    | { kind: 'Rectangle', height: number, width: number }

function printShape(s: Shape) {
    return document.write(
        match(s, {
        'Circle': c => `Circle(${c.radius})`,
        'Rectangle': r => `Rectangle(${r.width} x ${r.height})`
    }));
}

我当前对match函数的定义的尝试如下:

function match<T extends { kind: V }, V extends string, R>(
    x: T, branches: { [P in T['kind']]: (arg: T & { 'kind': P }) => R }) {

    return branches[x.kind](x);
}

这很接近,但是不幸的是效果不佳;虽然我已经成功让编译器抱怨给定的匹配是否完整,但是分支函数的参数类型不正确:参数cr的类型为any。 / p>

我可以将kind作为硬编码的标识符,但我通常在Typescript中不了解如何从通用类型联合中过滤掉可能性。例如,我将练习精简为尝试编写以下内容:

type Where<T, K extends keyof T, V extends T[K]> = ???

我的类型约束正确无误,因为在编写时,我从编译器获得了有关类型和文字的正确验证:

type Circle = Where<Shape, 'kind', 'Circle'>

但是我不明白我可以在该类型表达式的右边写什么来返回:

{ kind: 'Circle', radius: number }

1 个答案:

答案 0 :(得分:3)

要从联合中提取特定类型,可以使用Extract条件类型。这将提取作为第二个参数的子类的并集的所有成员。因此,您的where类型将如下所示:

type Where<T, K extends keyof T, V extends T[K]> = Extract<T, Record<K, V>>
type C = Where<Shape, 'kind', 'Circle'> //{ kind: 'Circle', radius: number }

如果使用返回函数的函数,则可以获得函数的完整类型。第一个调用集T,第二个调用可以使用类型信息来完全键入参数:

type Shape =
  | { kind: 'Circle', radius: number }
  | { kind: 'Rectangle', height: number, width: number }

function printShape(s: Shape) {
  var r = match(s)({
    'Circle': c => `Circle(${c.radius})`,
    'Rectangle': r => `Rectangle(${r.width} x ${r.height})`
  }) // r is string
  return document.write(r);
}


function match<T extends { kind: V }, V extends string>(x: T) {
  return function <R>(branches: { [P in T['kind']]: (arg: Extract<T, { 'kind': P }>) => R }) {

    return branches[x.kind](x as any);
  }
}