【问题标题】:Can this be strongly typed?这可以是强类型吗?
【发布时间】:2019-06-15 03:49:10
【问题描述】:

我正在尝试在 Typescript 中实现一种伪模式匹配,使用它们对可区分联合的支持,使用匹配函数以及表示匹配表达式分支的对象。

这是我希望能够使用的场景:

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})`
    }));
}

我目前对匹配函数定义的尝试如下所示:

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

我可以将kind 作为硬编码的鉴别器,但我通常不明白在 Typescript 中如何从泛型类型联合中过滤出可能性。例如,我将我的练习浓缩为尝试编写以下内容:

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

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

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

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

{ kind: 'Circle', radius: number }

【问题讨论】:

    标签: typescript types discriminated-union


    【解决方案1】:

    要从联合中提取特定类型,您可以使用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);
      }
    }
    

    【讨论】:

    • 就是这样,谢谢!回答的速度也做得很好。
    猜你喜欢
    • 2020-05-05
    • 2011-09-29
    • 1970-01-01
    • 2012-07-04
    • 2019-06-06
    • 1970-01-01
    • 2016-12-26
    • 2016-01-18
    相关资源
    最近更新 更多