【问题标题】:Typescript: using conditional typing in conditional statements打字稿:在条件语句中使用条件类型
【发布时间】:2019-09-26 13:06:40
【问题描述】:

假设我有很多联合类型:

var MyComplexType = MyType1 | MyType2 | MyType3 | ... | MyTypeN

MyType{N} 有这种签名的地方:

type MyType1 = {
    type: string,
    data: <different data for different types>
}

我知道我可以使用一种类型保护功能,例如。 g.:

function isMyComplexTypeOfMyType1(item: MyComplexType): item is MyType1 {
    return item.type == "type of MyType1"
}

但是在这种情况下我应该写很多这样的函数。

所以,问题是:我可以在条件语句(if ... elseswitch ... case)中动态定义类型吗?例如:

function someFunction(item: MyComplexType) {
    switch (item.type) {
        case "type of MyType1":
            // item is MyType1
            // do something
            break
        case "type of MyType2":
            // item is MyType2
            // do something
            break
        ...
    }
}

【问题讨论】:

    标签: typescript types conditional-types


    【解决方案1】:

    如果您打算使用switch/case 语句检查联合类型的值,您可能应该将其设为disciminated union,其中联合的每个组成部分的type 属性被声明为相关的string literal而不仅仅是string。您实际上并不需要条件类型来处理这个问题,至少在您的 someFunction() 实现中不需要。

    例如,假设您的类型如下所示:

    type MyType1 = { type: "type1", data: { a: string, b: number } };
    type MyType2 = { type: "type2", data: { c: boolean, d: string } };
    type MyType3 = { type: "type3", data: { e: number, f: boolean } };
    
    type MyComplexType = MyType1 | MyType2 | MyType3;
    

    然后编译器会自动将MyComplexType["type"] 上的检查视为类型保护,如下所示:

    const exhaustivenessCheck = (x: never) => x;
    
    function someFunction(item: MyComplexType) {
        switch (item.type) {
            case "type1":
                console.log(2 * item.data.b); // okay
                break;
            case "type2":
                console.log(item.data.d.charAt(0)); // okay
                break;
            case "type3":
                console.log(7 - item.data.e); // okay
                break;
            default:
                throw exhaustivenessCheck(item); // okay
        }
    }
    

    如果函数以某种方式落入default,则exhaustivenessCheck() 基本上是throw 语句。这不应该发生,但有用的是,如果编译器认为您没有检查所有内容,它会警告您。那是因为exhaustivenessCheck() 要求它的参数是never 类型,这是不可能的。如果您注释掉 case "type3" 子句,或者稍后在 MyComplexType 联合中添加一个新组成部分,exhaustivenessCheck() 行将抛出一个错误,指出您未能检查案例。


    此时你可以停下来,但如果你的类型真的是程序化的,因为它们只包含两个属性,一个type 判别字符串和一个data 属性,那么你可以像这样以更少的重复来定义你的类型:

    // a mapping from type string to data type
    type MyTypes = {
        type1: { a: string, b: number };
        type2: { c: boolean, d: string };
        type3: { e: number, f: boolean };
    }
    
    // convert the mapping to the union of types
    type MyType<K extends keyof MyTypes = keyof MyTypes> = {
        [P in K]: { type: P, data: MyTypes[P] }
    }[K]
    

    您可以验证 MyTypeMyType&lt;keyof MyTypes&gt; 是否扩展为我在上面定义的 MyComplexType 联合。您的旧 MyType1 现在是 MyType&lt;"type1"&gt;,依此类推。也就是说,如果您需要为类型使用旧名称,您可以这样做:

    type MyType1 = MyType<"type1">;
    type MyType2 = MyType<"type2">;
    type MyType3 = MyType<"type3">
    type MyComplexType = MyType;
    

    希望有所帮助;祝你好运!

    【讨论】:

    • 太棒了,这绝对有帮助!这两种方法都对我有用,非常感谢!
    猜你喜欢
    • 2019-05-03
    • 1970-01-01
    • 2020-06-25
    • 2021-09-18
    • 1970-01-01
    • 2022-01-22
    • 2023-02-13
    • 2021-11-06
    • 1970-01-01
    相关资源
    最近更新 更多