简体   繁体   English

如何分解TypeScript“Discriminated Union”切换块并同时保持其详尽无遗

[英]how to decompose TypeScript “Discriminated Union” switch block and keep it exhaustive at the same time

For my app I used a "Discriminated Union" pattern with exhaustiveness check as described in the TypeScript manual . 对于我的应用程序,我使用了“Discriminated Union”模式,并使用TypeScript 手册中描述的详尽检查。 Time went by, and eventually my switch ended up containing 50+ cases. 时间流逝,最终我的开关最终包含50多个案例。

So my question is: is there any good solution to decompose this switch without braking its exhaustiveness? 所以我的问题是:有没有什么好的解决方案来分解这个开关而不会制动它的详尽性?

In other words how to split it up, if this can help I can logically divide these unions on subtypes (for ex. shapes below can be divided for equilateral and others): 换句话说,如何分解它,如果这可以帮助我在逻辑上将这些联合分成子类型(例如,下面的形状可以分为等边和其他):

interface Square {
    kind: "square";
    size: number;
}
interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}
interface Circle {
    kind: "circle";
    radius: number;
}

//... 50 more shape kinds

type Equilateral = Square | Circle /*| 25 more...*/;
type Other = Rectangle /*| 25 more...*/;

type Shape = Equilateral |  Other;

function assertNever(x: never): never {
    throw new Error("Unexpected object: " + x);
}
function area(s: Shape) {
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
        /*
        ...
        ... a lot of code lines
        ...
        */
        default: return assertNever(s); 
    }
}

I just found out (through experimentation, not because it's mentioned in documentation anywhere) that you can indeed build a type hierarchy of discriminated unions using multiple discriminants : 我刚刚发现(通过实验,而不是因为它在任何地方的文档中提到过),你确实可以使用多个判别 构建一个有区别的联合的类型层次结构

interface Square {
    shape_kind: "equilateral";
    kind: "square";
    size: number;
}
interface Circle {
    shape_kind: "equilateral";
    kind: "circle";
    radius: number;
}
interface Rectangle {
    shape_kind: "rectangle";
    width: number;
    height: number;
}

type Equilateral = Square | Circle

type Shape = Equilateral | Rectangle;

function area(s: Shape) {
    switch (s.shape_kind) { // branch on "outer" discriminant
        case "equilateral":
            // s: Equilateral in here!
            return area_root(s) ** 2;
        case "rectangle":
            return s.height * s.width;
    }
}
function area_root(e: Equiliteral) {
    switch (s.kind) { // branch on "inner" discriminant
        case "square": return s.size;
        case "circle": return Math.sqrt(Math.PI) * s.radius;
    }
}

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

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