基于 class 中变量值的条件类型 (TypeScript)

Conditional type based on value of variable in a class (TypeScript)

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

state 定义为 "loading" | "loaded" | "error",我想根据 state.[=31= 的当前值更改 data 的类型]

例如,如果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'

我不确定这是否真的可行。如果不是,是否有其他解决方案?

我不认为有一种方法(或者至少不是一种干净的方法)让一个 class 属性 的类型依赖于另一个 属性.

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

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" }
}

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

使用此类型时,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 中很常用。