Typescript Discriminated Union 允许无效状态 [英] Typescript Discriminated Union allows invalid state

查看:18
本文介绍了Typescript Discriminated Union 允许无效状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆