为什么Typescript允许切片类型?

时间:2020-05-13 12:56:46

标签: typescript generics

有人可以向我解释为什么要在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
}

我希望setOnlyA中不允许进行setAB呼叫。我启用了strict模式。我需要其他设置吗?

4 个答案:

答案 0 :(得分:3)

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

Ryan Cavanaugh在this中进行了解释:

这是默认情况下的协变量类型系统的一个基本问题-隐式假设是通过超类型别名进行的写操作很少,这是正确的,除非不是这样。

严格遵守字段差异可能会给用户带来极大的痛苦,甚至对功能启用严格的差异也仅针对函数类型而不是针对方法(详细here

):

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

有人建议启用writeonly修饰符(并严格要求readonly)或具有显式的co / contra-contra-variant注释,因此稍后我们可能会得到一个严格的标志,但是目前这是TS团队做出的不明智/不实用的权衡。

答案 1 :(得分:0)

可以,因为{ a: number; b: string }{ a: number}的子类型。这就是打字稿的工作方式:https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#14-structural-subtyping

答案 2 :(得分:0)

系统关心的是您实现类型,而不是实现完全相同的类型,像接口一样考虑它-

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

答案 3 :(得分:0)

我的固定代码如下:

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作为设置器。使用常规成员函数不起作用-打字稿无法像原始代码中那样发现问题。