我想创建一个具有某些内部状态的类(可能是加载、错误或成功)
创建您可能的状态类型
type State<T> = ErrorState | SuccessState<T> | LoadingState;
type ErrorState = { status: "error"; error: unknown };
type SuccessState<T> = { status: "success"; data: T };
type LoadingState = { status: "loading" };
我还想在类上有一些可以检查此类状态的方法。
创建包含状态的 Foo 类
我假设在这里,您想调用某种公共类型保护方法isSuccess
, isLoading
,isError
它检查类实例状态,并可以通过使用 if/else 缩小 true 分支中的状态类型。您可以通过创建返回类型保护来做到这一点多态这个类型 https://www.typescriptlang.org/docs/handbook/advanced-types.html#polymorphic-this-types包含您的缩小状态的谓词。
// 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";
}
}
我们来测试一下:
const instance = new Foo({ status: "success", data: 42 });
if (instance.isSuccess()) {
// works, (property) data: number
instance.currentState.data;
}
操场 https://www.typescriptlang.org/play/#code/C4TwDgpgBAysCGwIB4AqA+KBeKBRATvgPb5yLQA+sArgMa0QDOjZSamVAMkfACYCWAOwDmrCAG4AUKEh5CJMdigBvKIwTBqjAFxQARBHn494qIeL5d1QQGtBRAO6CoAXykzoMOg2Zj2S1XVELV09Rm8mRhMoXkR4XVRXd3Bobj4hUQ1oHECNEP0AGx4BET0kyUkAekqoRP5GKGAAC2gwImZ+ACMC6FiERpSoIgAzNQjmNSzJWgL4CYAxIiI0JWs7R0FMZUkoKFoiQXV8OmASAAowam7+Wih8CD4DgpA96kIIQWAxXT8MAEoVC4Krt6mkSsIzn9dM16lBYapaG97p9vlAwRlFC4VDtdncIJp8M4YYwAHSI94orIkoKaBpYemFYoZEw4oE4+peeiRSHQpqw+GvClfLI-cYsLL+LHbXF4glEvmk8nI4Xkal5OkMsJilm7NkgxgECw8xoKuENBFIj4qpC6Q0KLKubEy+5yk31MmWymqmlabCa8wkHWuSRs-aHYBw8PwQQMJSCCAOKCLIhnXLBHT6cJc5h6AA0MTiugALAAmVx-KT8UZnIRBGMQEkcsWQgHSqDVKAOEg2Rj5i7ESD4UAAvrxKCCagAW06hnZUfrHqFYhJo9MIaAA
局限性
事情是这样的:只有当你宣布你是班级成员时,你才能这样做currentState
with 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
}
操场 https://www.typescriptlang.org/play/#code/C4TwDgpgBAysCGwIB4AqA+KBeKBRATvgPb5yLQA+sArgMa0QDOjZSamVAMkfACYCWAOwDmrCAG4AUKEh5CJMdigBvKIwTBqjAFxQARBHn494qIeL5d1QQGtBRAO6CoAXykzoMOg2Zj2S1XVELV09Rm8mRhMoXkR4XVRXd3Bobj4hUQ1oHECNEP0AGx4BET0kyUlaAvhmKAAxIiI0JWs7R0FMZUkoKFoiQXV8OmASAAowfH4AN3IofAg+foKQXupCCEFgMV0-DABKFRcKnuEIYDSS4VG93QuMxSpBagKClW6eubO152AAC35GAA6WhreabMSAoKaRjYLA4PRFdKlKAAfigfwBwNBGy2WSguieLykPSO71OWwizGuO0pLCy-kez1eXQ+n00+B+-yBIPW4KykLyMLh8PC9EiZTRGO52L5swJTOJrmOUHJBAs1LkFgeUEJzPePXm7M5mJ5YNx5AFwSFcP05hIEvRXKxvPNSHxOoV7yOpL6A2AUCEQUEDCUgggDnqjVGuStoVFPiiABoYnFdAAWABMrj2Ul96jUeJwgYQwYggPJXjFVL2kn4ADMoKModAAIQ23UHFlQAD03agDhINkYyfGxEg+FAB1iCHlAFsAEaGd7NwHT+CSI5AA
关于您的分支错误的旁注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
}
TypeScript 不推断foo.value
此处为 null,因为foo.hasValue()
是一个自定义类型保护,只是将您的类型缩小到{ value: string }
具有真实条件。如果条件为 false,则默认类型value
(string | null
)再次假设。自定义类型保护取消了 TypeScript 的正常分支逻辑。您可以通过简单地省略它来更改它:
if (foo.value !== null) {
// OK
foo.value.toString();
} else {
(foo.value); // (property) Foo.value: null
}
操场 https://www.typescriptlang.org/play/#code/MYGwhgzhAEBiD29oG8BQ1oAcCuAjEAlsNAG5gjYCmAXNBAC4BOBAdgObQA+0L2II0ALzQARGAAmAMwgiA3Oix5CxABaQAauSoAKAJS16KgjGMpSWmnSasOAXxQKMjSvWyMW0Q8YB0ZCpWgAQkFhXn55DFtUKNRgeBYGaElEIR5KAHc4RD15AklobWT4bzUITX89XQcMAHoa6AB5AGkFIt8Lb3p4AGVrdhzo6EoQCAC0DAw2vypo1FQ8gqmLIJCePhAq8eg6xpbJxHb-Tp6+tgH7YdHqxYPpyl1ZbfrtTEZ4TEpGegBPKoRiu60MIgaJAA
如果您从类实例内部检查您的状态
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;
//...
}
}
}
操场 https://www.typescriptlang.org/play/#code/C4TwDgpgBAysCGwIB4AqA+KBeKBRATvgPb5yLQA+sArgMa0QDOjZSamVAMkfACYCWAOwDmrCAG4AUKEh5CJMdigBvKIwTBqjAFxQARBHn494qIeL5d1QQGtBRAO6CoAXykzoMOg2Zj2S1XVELV09Rm8mRhMoXkR4XVRXd3Bobj4hUQ1oHECNEP0AGx4BET0kyUlaAvhmKAAxIiI0JWs7R0FMZUkoKFoiQXV8OmASAAowfH4AN3IofAg+foKQXupCCEFgMV0-DABKFRcKnuEILYjmUb2di5Ys-ypBagKClW6eubO152AAC35GAA6WhreabMSAoKaRjYLA4MK3MoAfigfwBwNBGy2WSguieLykPSO7wA9CSoAARCAAMyE0Am01mIPURAAtqiUlBhNR4PheIDSeSAOrQWjwQT2YBQLT0ojLVkkMD-Wio-4wjxQfpQBkzJBQRDASYAI2oSAANIK1EQoA56TUYUJVdAodABPNaMBlgKejrZgB9AFeeiRdijF07e77XQuqAAmjB3yRzrvHrzTT4ZwuyF5GFw+HhBNRQmuY7a6hGgr8FW8IgwNlnf4iK5vD6x6lQUZooEBli3TtqjHrcFZPYHLqtnpdwdg7HkQGxBDF1tkwGrlMl9dkzX4WOCSuCaB-UVssC8gFat0QD3Ldf8dv99EgoezpDZ4K5uH6As+KJj9eTgcnxnCEF3gJcPhXNdWyOIlJCOIA