Typescript Discriminated Union 允许无效状态 [英] Typescript Discriminated Union allows invalid state
问题描述
我正在尝试使用 Typescript Discriminated Union 来建模异步加载数据时比较常见的场景:
I am trying to use a Typescript Discriminated Union to model a rather common scenario when loading data asynchronously:
type LoadingState = { isLoading: true; }
type SuccessState = { isLoading: false; isSuccess: true; }
type ErrorState = { isLoading: false; isSuccess: false; errorMessage: string; }
type State = LoadingState | SuccessState | ErrorState;
根据我的理解,这应该根据类型定义限制允许的值组合.但是,类型系统很乐意接受以下组合:
According to my understanding, this should limit the allowed combinations of values according to the type definitions. However, the type system is happy to accept the following combination:
const testState: State = {
isLoading: true,
isSuccess: true,
errorMessage: "Error!"
}
我预计这里会出现错误.是不是我遗漏了什么或以某种方式滥用了类型定义?
I expect an error here. Is there something I am missing or in some way misusing the type definitions?
推荐答案
这是一个关于联合的额外属性检查工作方式的问题.如果将对象字面量分配给联合类型的变量,则如果该属性存在于任何 联合成员上,则该属性不会被标记为多余.如果我们不认为多余的属性是一个错误(并且除了对象字面量它们不被视为错误),您指定的对象字面量可以是 LoadingState
的实例(带有 LoadingState
的实例>isLoading 按照规定设置为 true
和一些多余的属性).
This is an issue with the way excess property checks work on unions. If an object literal is assigned to a variable of union type, a property will not be marked as excess if it is present on any of the union members. If we don't consider excess properties to be an error (and except for object literals they are not considered an error), the object literal you specified could be an instance of LoadingState
(an instance with isLoading
set to true
as mandated and a couple of excess properties).
为了避免这种不良行为,我们可以向 LoadingState
添加属性,使您的对象字面量与 LoadingState
To get around this undesired behavior we can add properties to LoadingState
to make your object literal incompatible with LoadingState
type LoadingState = { isLoading: true; isSuccess?: undefined }
type SuccessState = { isLoading: false; isSuccess: true; }
type ErrorState = { isLoading: false; isSuccess: false; errorMessage: string; }
type State = LoadingState | SuccessState | ErrorState;
const testState: State = { // error
isLoading: true,
isSuccess: true,
errorMessage: "Error!"
}
我们甚至可以创建一个类型来确保添加这样的成员
We could even create a type that would ensure such member will be added
type LoadingState = { isLoading: true; }
type SuccessState = { isLoading: false; isSuccess: true; }
type ErrorState = { isLoading: false; isSuccess: false; errorMessage: string; }
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> = T extends any ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, undefined>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>
type State = StrictUnion< LoadingState | SuccessState | ErrorState>
const testState: State = { // error
isLoading: true,
isSuccess: true,
errorMessage: "Error!"
}
这篇关于Typescript Discriminated Union 允许无效状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!