Typescript区分联合允许无效状态

Typescript区分联合允许无效状态,typescript,discriminated-union,Typescript,Discriminated Union,在异步加载数据时,我尝试使用Typescript来模拟一个相当常见的场景: type LoadingState = { isLoading: true; } type SuccessState = { isLoading: false; isSuccess: true; } type ErrorState = { isLoading: false; isSuccess: false; errorMessage: string; } type State = LoadingState | S

在异步加载数据时,我尝试使用Typescript来模拟一个相当常见的场景:

type LoadingState = { isLoading: true; }
type SuccessState = { isLoading: false; isSuccess: true; }
type ErrorState =   { isLoading: false; isSuccess: false; errorMessage: string; }

type State = LoadingState | SuccessState | ErrorState;
根据我的理解,这应该根据类型定义限制允许的值组合。但是,类型系统乐于接受以下组合:

const testState: State = {
    isLoading: true,
    isSuccess: true,
    errorMessage: "Error!"
}

我想这里有个错误。我是否遗漏了某些内容或在某种程度上误用了类型定义?

这是多余属性检查在联合上工作的问题。如果将对象文字指定给联合类型的变量,则如果某个属性出现在任何联合成员的上,则该属性不会被标记为多余。如果我们不认为多余的属性是一个错误(除了对象文字,它们不被认为是一个错误),您指定的对象文字可以是<代码>加载状态>代码>(一个实例,代码为<加载< <代码> >设置为<代码>真< /代码>。 为了避免这种不希望出现的行为,我们可以向
LoadingState
添加属性,使您的对象文字与
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!"
}
我们甚至可以创建一个类型来确保添加这样的成员

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!"
}
type LoadingState={isLoading:true;}
类型SuccessState={isLoading:false;isSuccess:true;}
类型ErrorState={isLoading:false;isSuccess:false;errorMessage:string;}
类型UnionKeys=T扩展T?基特:永远不会;
类型StrictUnionHelper=T扩展了任何?T&Partial:从不;
类型StrictUnion=StrictUnionHelper
类型状态=StrictUnion
const testState:State={//错误
孤岛加载:是的,
isSuccess:对,
错误信息:“错误!”
}

您是否启用了
suppressExcessPropertyErrors
编译器选项?@cartant-刚刚尝试将其设置为true和false,但没有任何区别。(自2017年以来,这一直是TypeScript中的一个公开问题,请参阅此处的进一步讨论:)问题似乎在v3.5中得到了解决:非常好的解释,我将试一试。非常感谢。这太棒了。遗憾的是,它没有在任何地方的软件包中发布。我已经在上为它做了一个请求,但它似乎在TypeScript 3.4中停止了工作@KarolMajewski感谢您引起我的注意,我们将对此进行调查…@KarolMajewski这在RC 3.4的当前版本下工作,但我计划报告一个问题。。这似乎是类型别名解析方式的突破性变化:
type-StrictUnionHelper=T扩展了任何?T&Partial:从不;键入StrictUnion=StrictUnionHelper