【问题标题】:Typescript Discriminated Union allows invalid state打字稿歧视联盟允许无效状态
【发布时间】:2019-03-11 16:00:45
【问题描述】:

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

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!"
}

我预计这里会出错。有什么我遗漏的或以某种方式滥用类型定义的吗?

【问题讨论】:

标签: typescript discriminated-union


【解决方案1】:

这是对工会进行过多财产检查的方式的问题。如果将对象字面量分配给联合类型的变量,则如果属性存在于 any 联合成员中,则该属性不会被标记为多余。如果我们不认为多余的属性是错误(并且除了对象字面量之外,它们不被视为错误),您指定的对象字面量可能是 LoadingState 的实例(isLoading 设置为 @987654324 的实例@ 强制要求和一些多余的属性)。

为了避免这种不良行为,我们可以向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!"
}

【讨论】:

  • 非常好的解释,我会给StrictUnion一个机会。谢谢!
  • 这很棒。可惜它没有在任何地方的包中发布。我已经在github.com/andnp/SimplyTyped/issues/76#issue-396291343 完成了一个请求
  • 它似乎在 TypeScript 3.4 中停止工作了。 ? 没有错误了。
  • @KarolMajewski 感谢您引起我的注意,会调查它...
  • @KarolMajewski 这适用于当前的 RC 3.4,但我计划报告一个问题 .. 这似乎是类型别名解决方式的重大变化:type StrictUnionHelper&lt;T, TAllKeys extends PropertyKey&gt; = T extends any ? T &amp; Partial&lt;Record&lt;Exclude&lt;TAllKeys, keyof T&gt;, never&gt;&gt; : never; type StrictUnion&lt;T&gt; = StrictUnionHelper&lt;T, UnionKeys&lt;T&gt;&gt;
猜你喜欢
  • 2019-04-18
  • 1970-01-01
  • 2019-07-28
  • 2020-11-30
  • 2023-02-19
  • 2019-07-14
  • 2021-11-15
  • 1970-01-01
相关资源
最近更新 更多