Почему typescript позволяет ссылаться/назначать тип/свойство только для чтения по изменяемому типу?

#typescript #readonly

Вопрос:

Почему typescript не применяет ключевое слово только для чтения и не запрещает нам передавать свойство только для чтения в свойство, не предназначенное только для чтения, это противоречит сути

 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!```
 

Комментарии:

1. Я написал правило ESLint, чтобы предотвратить этот сценарий: github.com/danielnixon/…

Ответ №1:

Это известное поведение, удивительные эффекты которого вдохновили проблему, первоначально озаглавленную «модификаторы только для чтения-это шутка». Короткий ответ на вопрос: «это нарушило бы обратную совместимость, когда readonly было введено». Длинный ответ следует из следующего комментария:

@ahelsberg сказал:

Для обеспечения обратной совместимости 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);
 

В существующем коде машинописного текста нет способа указать, предназначено ли конкретное свойство только для чтения или изменяемо. В приведенном выше коде foo массив, который он передается, не мутирует, но в коде нет ничего, что говорит, что это невозможно. Однако теперь, когда мы добавили readonly модификатор в length свойство и подпись индекса в String интерфейсе (поскольку они действительно доступны только для чтения), foo(s) приведенный выше вызов был бы ошибкой, если бы мы сказали, что readonly свойство несовместимо со свойством без readonly . В частности, мы не можем интерпретировать отсутствие readonly модификатор, означающий чтение-запись, мы можем только сказать, что мы не знаем. Итак, если интерфейс отличается от другого интерфейса только readonly модификаторами его свойств, мы должны сказать, что эти два интерфейса совместимы. Все остальное было бы огромным прорывным изменением.

Так что вот оно что. Если вы хотите продемонстрировать свою поддержку в решении этой проблемы, вы можете перейти к этой проблеме на GitHub и дать ей 👍 или описать свой вариант использования, если он убедителен и еще не упомянут.

В любом случае, надеюсь, это поможет; удачи!

Ответ №2:

readonly Ключевое слово-это только проверка компилятора ts. Сказав это, вы сообщаете компилятору, что функция mutate принимает параметр, не предназначенный только для чтения.

Однако, если вы на самом деле введете его правильно, компилятор будет жаловаться, например

 type test = { readonly bar: number };

let foo: test = {
    bar: 123
};

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

Комментарии:

1. я хочу сказать, что вы не можете гарантировать неизменность ниже по потоку, хотя они могли бы обеспечить проверку времени компиляции только для чтения в рамках проверки информации о типе, и я спрашиваю, почему они этого не сделали