我想创建一个具有某些内部状态的类(可能正在加载,错误或成功)。我还想在类上有一些方法可以检查此类的状态。
理想的API:
function f(x: LoadingError<number>) {
if (x.isLoading()) {
} else if (x.isError()) {
} else {
(x.data); // TypeScript knows x.data is of type `number`
}
}
Run Code Online (Sandbox Code Playgroud)
我一直在努力的主要事情是创建isLoading
和isError
方法,以便TypeScript可以理解它们。
我尝试在实际的类结构(“ " this is { ... }
”)上编写一些用户定义的类型防护:
class Foo {
public value: string | null;
public hasValue(): this is { value: string } {
return this.value !== null;
}
}
const foo = new Foo();
if (foo.hasValue()) {
// OK
foo.value.toString();
} else {
(foo.value); // TypeScript does NOT understand that this can only be null
}
Run Code Online (Sandbox Code Playgroud)
但是,由于TypeScript会“忘记”该else
子句中的类实例的状态,因此无法使用。
我的硬性要求之一是为此使用类,因为我不想拥有isLoading(instance)
or isError(instance)
方法,而是想要instance.isLoading()
and instance.isError()
。
我想创建一个具有某些内部状态的类(可能正在加载,错误或成功)
type State<T> = ErrorState | SuccessState<T> | LoadingState;
type ErrorState = { status: "error"; error: unknown };
type SuccessState<T> = { status: "success"; data: T };
type LoadingState = { status: "loading" };
Run Code Online (Sandbox Code Playgroud)
我还想在类上有一些方法可以检查此类的状态。
我想在这里,你要调用某种公共型导引法的isSuccess
,isLoading
,isError
支票类实例的状态,并且可以通过使用缩小的真实分支的状态类型的if / else。您可以通过创建类型防护来做到这一点,该防护返回包含您的缩小状态的多态此类型谓词。
// T is the possible data type of success state
class Foo<T = unknown> {
constructor(public readonly currentState: State<T>) {}
isLoading(): this is { readonly currentState: LoadingState } {
return this.currentState.status === "loading";
}
isSuccess(): this is { readonly currentState: SuccessState<T> } {
return this.currentState.status === "success";
}
isError(): this is { readonly currentState: ErrorState } {
return this.currentState.status === "error";
}
}
Run Code Online (Sandbox Code Playgroud)
让我们测试一下:
const instance = new Foo({ status: "success", data: 42 });
if (instance.isSuccess()) {
// works, (property) data: number
instance.currentState.data;
}
Run Code Online (Sandbox Code Playgroud)
这很重要:只有在currentState
使用public修饰符声明了类成员(TypeScript限制)后,您才能这样做!如果已将其声明为private,则不能为此目的使用此类类型防护。另一种选择是返回一个可选状态:
class Foo<T = unknown> {
...
getSuccess(): SuccessState<T> | null {
return this.currentState.status === "success" ? this.currentState : null;
}
...
}
// test it
const instance = new Foo({ status: "success", data: 42 });
const state = instance.getSuccess()
if (state !== null) {
// works, (property) data: number
state.data
}
Run Code Online (Sandbox Code Playgroud)
foo.hasValue()
:const foo = new Foo();
if (foo.hasValue()) {
// OK
foo.value.toString();
} else {
(foo.value); // TypeScript does NOT understand that this can only be null
}
Run Code Online (Sandbox Code Playgroud)
TypeScript不会foo.value
在这里推断为null,因为这foo.hasValue()
是一个自定义的Type Guard,它只会将您的类型缩小为{ value: string }
true。如果条件为假,则再次采用默认类型value
(string | null
)。自定义类型防护取消了TypeScript的常规分支逻辑。您可以通过简单地省略它来更改它:
if (foo.value !== null) {
// OK
foo.value.toString();
} else {
(foo.value); // (property) Foo.value: null
}
Run Code Online (Sandbox Code Playgroud)
class Foo<T = unknown> {
...
// Define private custom type guard. We cannot use a polymorphic
// this type on private attribute, so we pass in the state directly.
private _isSuccess(state: State<T>): state is SuccessState<T> {
return state.status === "success";
}
public doSomething() {
// use type guard
if (this._isSuccess(this.currentState)) {
//...
}
// or inline it directly
if (this.currentState.status === "success") {
this.currentState.data;
//...
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以创建一个可以处理三种情况的类型:
type AsyncValue<T> = Success<T> | Loading<T> | Failure<T>;
Run Code Online (Sandbox Code Playgroud)
然后您可以使用自定义防护来定义所有这些类型:
class Success<T> {
readonly value: T;
constructor(value: T) {
this.value = value;
}
isSuccess(this: AsyncValue<T>): this is Success<T> {
return true;
}
isLoading(this: AsyncValue<T>): this is Loading<T> {
return false;
}
isError(this: AsyncValue<T>): this is Failure<T> {
return false;
}
}
class Loading<T> {
readonly loading = true;
isSuccess(this: AsyncValue<T>): this is Success<T> {
return false;
}
isLoading(this: AsyncValue<T>): this is Loading<T> {
return true;
}
isError(this: AsyncValue<T>): this is Failure<T> {
return false;
}
}
class Failure<T> {
readonly error: Error;
constructor(error: Error) {
this.error = error;
}
isSuccess(this: AsyncValue<T>): this is Success<T> {
return false;
}
isLoading(this: AsyncValue<T>): this is Loading<T> {
return false;
}
isError(this: AsyncValue<T>): this is Failure<T> {
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
现在您可以AsyncValue
在代码中使用:
function doSomething(val: AsyncValue<number>) {
if(val.isLoading()) {
// can only be loading
} else if (val.isError()) {
// can only be error
val.error
} else {
// can only be the success type
val.value // this is a number
}
}
Run Code Online (Sandbox Code Playgroud)
可以通过其中之一调用:
doSomething(new Success<number>(123))
doSomething(new Loading())
doSomething(new Failure(new Error('not found')))
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
376 次 |
最近记录: |