为什么打字稿允许按可变类型引用/分配只读类型/属性?

时间:2019-07-23 14:26:45

标签: typescript readonly

为什么打字稿不强制使用readonly关键字,并阻止我们将readonly属性传递给非readonly属性,所以这无济于事

let foo: {
    readonly bar: number;
} = {
        bar: 123
    };

function iMutateFoo(foo: { bar: number }) {
    foo.bar = 456;
}

iMutateFoo(foo); // The foo argument is aliased by the foo parameter
console.log(foo.bar); // 456!```

2 个答案:

答案 0 :(得分:1)

readonly关键字仅是ts编译器检查。上面说的就是告诉编译器mutate函数接受一个非只读参数。

但是,如果您实际上正确键入它,则编译器会报错例如

type test = { readonly bar: number };

let foo: test = {
    bar: 123
};

function iMutateFoo(foo: test) {
    foo.bar = 456; // error
}

答案 1 :(得分:1)

这是一个known behavior,其令人惊讶的效果激发了最初名为"readonly modifiers are a joke"的问题。这个问题的简短答案是“引入readonly时,它会破坏向后兼容性”。长答案来自以下评论:

@ahelsberg said

  

为了确保向后兼容,readonly修饰符不会影响包含类型的子类型和可分配性类型的关系(但当然会影响对单个属性的分配)。

     

考虑以下代码:

interface ArrayLike<T> {
  length: number;
  [index: number]: T;
}

function foo(array: ArrayLike<string>) {
    // Doesn't mutate array
}

var s = "hello";
var a = ["one", "two", "three"];
foo(s);  // s has readonly length and index signature
foo(a);
     

在现有的TypeScript代码中,无法指示特定的属性是只读还是可变的。在上面的代码中,foo不会使传递的数组发生突变,但是代码中没有任何内容表明它不能这样做。但是,现在我们已经为readonly属性和length接口中的索引签名(因为它们实际上是只读的)添加了String修饰符,{{1}如果我们说foo(s)属性与没有readonly的属性不兼容,则上面的}调用将是一个错误。具体来说,我们无法将readonly修饰符的缺失解释为可读写,我们只能说不知道。因此,如果一个接口仅在属性上的readonly修饰符上与另一个接口不同,则必须说这两个接口是兼容的。其他任何事情都是巨大的突破性变化。

因此,您已经拥有了它。如果要显示对此修复程序的支持,则可能需要转到该GitHub issue,并给它加上?或描述用例(如果它很有吸引力并且尚未提及)。

无论如何,希望能有所帮助;祝你好运!