我很难将一个类的实例缩小到其受歧视的联合。
我具有以下已区别的工会:
interface ILoadableLoading<T> {
state: "Loading";
id: number;
}
interface ILoadableLoaded<T> {
state: "Loaded";
id: number;
item: T;
}
interface ILoadableErrored<T> {
state: "Error";
id: number;
error: string;
}
export type ILoadableDiscriminated<T> =
| ILoadableLoading<T>
| ILoadableLoaded<T>
| ILoadableErrored<T>;
type ILoadableState<T> = ILoadableDiscriminated<T>["state"];
以及以下课程:
class Loadable<T> {
state: ILoadableState<T> = "Loading";
id: number = 0;
item?: T | undefined;
error?: string | undefined;
}
现在我如何才能将该类的实例缩小到其各自的ILoadableDiscriminated<T>
联合,从而保持某种类型安全(不使用任何类型)?
例如我有以下创建方法,并希望返回已区分的联合:
unction createLoadable<T>(someState: boolean): ILoadableDiscriminated<T> {
var loadable = new Loadable<T>();
if (someState) {
loadable.state = "Error";
loadable.error = "Some Error";
// Would like to remove this cast, as it should narrow it out from state + defined error above
return loadable as ILoadableErrored<T>;
}
if (loadable.state === "Loading") {
// Would like to remove this cast, as it should narrow it from state;
return loadable as ILoadableLoading<T>;
}
if (loadable.state === "Loaded" && loadable.item) {
// Would like to remove this cast, as it should narrow it from state;
return loadable as ILoadableLoaded<T>;
}
throw new Error("Some Error");
}
可以在以下位置找到示例:https://codesandbox.io/embed/weathered-frog-bjuh0
文件:src/DiscriminatedUnion.ts
答案 0 :(得分:1)
问题是Loadable<T>
与定义的接口之间没有关系,这可以保证函数createLoadable()
在返回项目之前将每个属性设置为正确的状态。例如,Loadable<string>
可以具有以下值:
var loadable = new Loadable<string>();
loadable.state = "Error";
lodable.item = "Result text.";
return loadable;
上面没有任何接口,但它是有效的Loadable
实例。
我的方法是:
简化界面,只有一个必须是通用的:
interface ILoadableLoading {
state: "Loading";
id: number;
}
interface ILoadableLoaded<T> {
state: "Loaded";
id: number;
item: T;
}
interface ILoadableErrored {
state: "Error";
id: number;
error: string;
}
export type ILoadableDiscriminated<T> =
| ILoadableLoading
| ILoadableLoaded<T>
| ILoadableErrored;
type ILoadableState<T> = ILoadableDiscriminated<T>["state"];
为每个接口创建单独的类,以确保创建的对象遵守接口定义:
class LoadableLoading implements ILoadableLoading {
state: "Loading" = "Loading";
id: number = 0;
}
class LoadableLoaded<T> implements ILoadableLoaded<T> {
constructor(public item: T){}
state: "Loaded" = "Loaded";
id: number = 0;
}
class LoadableErrored implements ILoadableErrored {
constructor(public error: string){}
state: "Error" = "Error";
id: number = 0;
}
然后我们可以将函数与重载一起使用,以表明意图:
function createLoadable<T>(someState: true, state: ILoadableState<T>, item?: T): ILoadableErrored;
function createLoadable<T>(someState: false, state: "Loading", item?: T): ILoadableLoading;
function createLoadable<T>(someState: false, state: "Loaded", item?: T): ILoadableLoaded<T>;
function createLoadable<T>(someState: boolean, state?: ILoadableState<T>, item?: T): ILoadableDiscriminated<T> {
if (someState) {
return new LoadableErrored("Some error");
}
if (state === "Loading") {
// Would like to remove this cast, as it hsould figure it out from state;
return new LoadableLoading();
}
if (state === "Loaded" && item) {
// Would like to remove this cast, as it hsould figure it out from state;
return new LoadableLoaded(item);
}
throw new Error("Some Error");
}
最后,根据您为createLoadable()
函数输入的参数,类型将是返回类型,将自动区分类型:
const lodableError = createLoadable<string>(true, "Loading");
console.log(lodableError.error);
const lodableLoading = createLoadable<string>(false, "Loading");
console.log("Loading");
const loadableLoaded = createLoadable<string>(false, "Loaded", "MyResponse");
console.log(loadableLoaded.item)
请注意,参数重载了Typescript编译器的状态意图,但是您需要确保函数主体中的代码能够执行声明的内容。