【问题标题】:Conditional type based on value of variable in a class (TypeScript)基于类中变量值的条件类型(TypeScript)
【发布时间】:2019-05-08 01:17:52
【问题描述】:

我正在创建一个名为Loadable<T> 的通用类,它有两个字段。一个名为state,包含可加载资源的当前状态。第二个名为data,包含可加载资源的值。

state 定义为"loading" | "loaded" | "error",我想输入data 以根据state 的当前值进行更改。

例如,如果state"loading",那么data 应该是null。我无法弄清楚执行此操作的语法。

我创建了一个名为LoadableState<T, U>type,其定义为:

    type LoadableState<T, U> =
        T extends "loaded" ? U :
        T extends "loading" ? null :
        T extends "error" ? string :
        never;

其中传递的T 必须是"loaded" | "loading" | "error" 类型。

这部分有效,但试图从中定义data 无效。

export class Loadable<T> {
    public state: "loaded" | "loading" | "error" = "loading";

    public data: LoadableState<state, T>;
    //                         ^^^^^ Cannot find name 'state`
}

TypeScript 抛出以下错误:Cannot find name 'state'


我尝试过的其他事情:

public data: LoadableState<this.state, T>;
// Duplicate identified 'state'

public data: LoadableState<typeof this.state, T>;
// Cannot find name 'this'

public data: LoadableState<typeof state, T>;
// Cannot find name 'state'

public data: state extends "loaded" ? T :
    state extends "loading" ? null :
    state extends "error" ? string :
    never;
// Cannot find name 'state'

我不确定这是否真的可行。如果不是这样,有没有其他方法可以解决这个问题?

【问题讨论】:

    标签: typescript conditional-types


    【解决方案1】:

    我认为没有一种方法(或至少不是一种干净的方法)让一个类属性的类型依赖于另一个属性的当前值。

    最好将这两个属性组合到一个对象中:

    type LoadState<T> = {
        state: "loading",
    } | {
        state: "loaded",
        data: T
    } | {
        state: "error",
        data: string; // or maybe 'message'
    }
    
    export class Loadable<T> {
        state: LoadState<T> = { state: "loading" }
    }
    

    虽然类包装器在这里实际上并不是很有用,但您可能会考虑根本不使用该类。

    使用此类型时,Typescript 将强制您在尝试访问数据之前检查state

    function handleLoadState<T>(loadState: LoadState<T>) {
       loadState.data // ERROR, can't access data, since it might not exist
       if(loadState.state === "loaded")
           loadState.data // okay
       }
    }
    

    这种模式称为Discriminated Union,在 Typescript 中非常常用。

    【讨论】:

    • 这是解决这个问题的一个很好的解决方案。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-27
    • 2019-06-19
    • 1970-01-01
    • 1970-01-01
    • 2016-08-19
    相关资源
    最近更新 更多