基于 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 中很常用。
我正在创建一个名为 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 中很常用。