当我玩TypeScript的时候,我发现这个代码确实可以编译,但当然会抛出一个错误.

type Foo = {
    kind: "foo",
    foo: { foo: string };
}

type Bar = {
    kind: "bar",
    bar: { bar: string};
}

type Union = Foo | Bar;

function doSomething(fooOrBar: Union) {
    fooOrBar.kind = "foo";
    if(fooOrBar.kind === "foo")
        console.log(fooOrBar.foo.foo);
}

doSomething({
    kind: "bar",
    bar: { bar: "bar" }
});

我想这是可能的,因为fooOrBar包含FooBar之间的共享属性,因此看起来像{kind: "foo" | "bar"},这确实允许赋值?

在我的情况下,我可以通过将kind标记为readonly来规避这个问题,但这感觉更像是一种变通方法,而不是我真正想要的.有没有什么方法可以明确地定义union仅仅是两种类型中的一种,从而禁止对kind的赋值?

Playground

推荐答案

TypeScript的类型系统故意设置为unsound,允许不安全的代码存在,以支持一些惯用的JavaScript,这些JavaScript理论上在技术上不安全,但在实践中却非常有用,以至于他们觉得权衡是值得的.有关这种不健全的本质的更多信息,请参见microsoft/TypeScript#9825Why are TypeScript arrays covariant?. 使用TypeScript,您无法保证安全.这更像是你创造了安全more likely而不是你没有它.在大多数情况下,你能做的最好的是编写代码discourages不安全使用.

允许你写一个discriminated union的判别属性,这确实是不安全的.但是不合理的属性写入在TypeScript中是一个现实,允许对象在其属性类型中被视为协变而不是不变的(参见Difference between Variance, Covariance, Contravariance, Bivariance and Invariance in TypeScript).不变的属性类型是安全的,但使用TypeScript非常痛苦.


我浏览了TypeScript GitHub issue list,没有找到任何专门报告这个问题的东西,其中写入判别属性可能导致一个值的类型与任何unions 成员不匹配.如果你对此有强烈的感觉,你可以自己开一个问题,尽管事实上似乎没有一个存在的问题意味着,实际上,人们不会编写你所担心的那种代码.因此,很有可能它会以一个注释结束,大意是"是的,这是TS的一个设计限制,不值得修复,因为治疗会比疾病更糟糕". 或许就不是了. 无论如何,如果有人真的打开这样的问题,这将给这个问题一个权威的答案.

在缺乏权威答案的情况下,我们能得到的最接近的方法是参考其他已知的不健全性以及围绕它们的最佳实践. 如果你担心写信给一个物业,你可以把它写成readonly:

function doSomething(fooOrBar: Readonly<Union>) {
    fooOrBar.kind = "foo"; // error!
    if (fooOrBar.kind === "foo")
        console.log(fooOrBar.foo.foo);
}

同样,readonly只是一种阻止财产写作的方式.它不能保证任何事情.见Why typescript allows referencing/assigning a readonly type/property by mutable type?.语言中弥漫着不健全的现象.

Playground link to code

Typescript相关问答推荐

当类型断言函数返回假时,TypScript正在推断从不类型

在类型内部使用泛型类型时,不能使用其他字符串索引

泛型和数组:为什么我最终使用了`泛型T []`而不是`泛型T []`?<><>

动态调用类型化函数

如何根据另一个属性的布尔值来缩小函数属性的返回类型?

类型脚本类型字段组合

被推断为原始类型的交集的扩充类型的交集

使用TypeScrip5强制使用方法参数类型的实验方法修饰符

对象类型的TypeScrip隐式索引签名

如何缩小函数的返回类型?

TYPE FOR ARRAY,其中每个泛型元素是单独的键类型对,然后在该数组上循环

tailwind css未被应用-react

为什么TypeScrip假定{...省略类型,缺少类型,...选取类型,添加&>}为省略类型,缺少类型?

重写返回任何

编剧- Select 动态下拉选项

基于闭包类型缩小泛型类型脚本函数的范围

我应该使用服务方法(依赖于可观察的)在组件中进行计算吗?

有没有办法从不同长度的元组的联合中提取带有类型的最后一个元素?

React router - 如何处理嵌套路由?

React 中如何比较两个对象数组并获取不同的值?