【问题标题】:Why does Typescript allow slicing of types?为什么 Typescript 允许对类型进行切片?
【发布时间】:2020-08-29 16:23:13
【问题描述】:

有人可以解释一下为什么这会在 Typescript 中编译吗?

class Result<T> {
    object: T | null = null;
}

function setOnlyA(res: Result<{ a: number }>) {
    res.object = { a: 5 };
}

function setAB(res: Result<{ a: number; b: string }>) {
    setOnlyA(res);
    // everything compiles fine, but res object is invalid 
    // at this point according to type definitions
}

我希望在setAB 中不允许调用setOnlyA。我开启了strict 模式。我需要其他设置吗?

【问题讨论】:

  • 正如提香所说,这是一种选择,它会产生类型问题,同时让一些事情变得更方便。有趣的是,Swift 做出了相反的选择,并避免了这些问题(虽然不太方便),一个非常常见的问题是“为什么不能在 Swift 中编译?” stackoverflow.com/questions/30487258/swift-generics-upcasting/…

标签: typescript generics


【解决方案1】:

没关系,因为{ a: number; b: string }{ a: number} 的子类型。这就是 Typescript 的工作原理:https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#14-structural-subtyping

【讨论】:

  • 是的,但在这种情况下没有意义,因为 Typescript 无法确保运行时结果与类型定义匹配,即调用 setOnlyA 后 res 上缺少成员 b。
【解决方案2】:

不幸的是,这是 typescript 类型系统的一个基本问题。假定字段是协变的,即使可读和可写字段实际上应该使类型保持不变。 (如果您想了解协变和逆变,请参阅answer)。

Ryan Cavanaugh 在this 中解释:

这是默认协变类型系统的一个基本问题 - 隐含的假设是通过超类型别名写入的情况很少见,除了不是这样的情况外,这是正确的。

对字段变化非常严格可能会给用户带来很大的痛苦,即使对函数启用严格的变化也只是针对函数类型而不是方法,详见here

更严格的检查适用于所有函数类型,但源自方法或构造函数声明的函数类型除外。专门排除方法以确保泛型类和接口(例如 Array)

有建议启用writeonly 修饰符(并且对readonly 更严格)或具有明确的 co/contra-variant 注释,因此我们可能会在以后获得严格标志,但目前这是一个TS 团队所做的不健全/可用性权衡。

【讨论】:

  • 非常感谢!我将尝试通过传递某种结果对象设置器函数来解决我的问题 - 应该可以让它抱怨。
  • @PauliusLiekis 这实际上很简单,因为函数参数类型(如果您面对 strictFunctionsEabled)是逆变的:typescriptlang.org/play/…
【解决方案3】:

系统关心你实现的类型,而不是完全相同的类型,把它想象成一个接口 -

  1. 您必须实现其属性。
  2. 您可以向实现它的类添加属性。

【讨论】:

  • 您似乎没有理解问题所在:在这种情况下,Typescript 无法确保类型安全。查看票数最高的答案。
  • 我做了...接口只影响打字稿上的类型安全...示例仍然有效。
【解决方案4】:

我的固定代码如下所示:

class Result<T> {
    private object: T | null = null;

    // this solves the problem
    setObject = (o: T) => {
        this.object = o;
    };

    // this doesn't
    //setObject(o: T) {     
    //  this.object = o;
    //};
}

function setOnlyA(res: Result<{ a: number }>) {
    res.setObject({ a: 5 });
}

function setAB(res: Result<{ a: number; b: string }>) {
    setOnlyA(res);
}

即解决方案是使用 lambda 作为设置器。使用常规成员函数不起作用 - 打字稿无法像原始代码一样发现问题。

【讨论】:

    猜你喜欢
    • 2019-08-06
    • 2016-11-22
    • 2016-07-12
    • 2017-01-18
    • 2021-05-30
    • 2017-09-08
    • 2015-07-24
    • 2018-07-27
    相关资源
    最近更新 更多