Система типов TypeScript позволяет помечать отдельные элементы интерфейса доступными только для чтения. Это позволяет вам работать в функциональном стиле (в котором неожиданные мутации это плохо):
1 2 3 4 5 6 7 8 910
functionfoo(config:{readonlybar:number;readonlybas:number;}){// ..}letconfig={bar:123,bas:123};foo(config);// Вы можете быть уверены, что `config` не изменился 🌹
Вы также можете использовать readonly в определениях interface и type, например:
1 2 3 4 5 6 7 8 91011
typeFoo={readonlybar:number;readonlybas:number;};// Инициализация в порядкеletfoo:Foo={bar:123,bas:456};// Мутация нетfoo.bar=456;// Ошибка: Выражение присваивания не может осуществлено// для константы или свойства, доступного только для чтения
Вы даже можете объявить свойство класса как readonly. Вы можете инициализировать их в момент объявления или в конструкторе, как показано ниже:
Существует тип Readonly, который принимает тип T и помечает все его элементы как readonly. Вот демонстрация использования этого на практике:
1 2 3 4 5 6 7 8 9101112
typeFoo={bar:number;bas:number;};typeFooReadonly=Readonly<Foo>;letfoo:Foo={bar:123,bas:456};letfooReadonly:FooReadonly={bar:123,bas:456};foo.bar=456;// OkayfooReadonly.bar=456;// ОШИБКА: bar только для чтения
ReactJS - это библиотека, которая любит иммутабельность, вы можете пометить ваши Props и State как неизменяемые, например:
1 2 3 4 5 6 7 8 910111213141516
interfaceProps{readonlyfoo:number;}interfaceState{readonlybar:number;}exportclassSomethingextendsReact.Component<Props,State>{someMethod(){// Вы можете быть уверены, что никто не сможет сделать следующееthis.props.foo=123;// ОШИБКА: (props неизменяемые)this.state.baz=456;// ОШИБКА: (следует использовать this.setState)}}
Однако вам не нужно это делать, поскольку определения типов для React уже помечают их как readonly (внутренняя оболочка универсального типа соответствует типу Readonly, упомянутому выше). Поэтому, достаточно:
1 2 3 4 5 6 7 8 910
exportclassSomethingextendsReact.Component<{foo:number},{baz:number}>{// Вы можете быть уверены, что никто не сможет сделать следующееsomeMethod(){this.props.foo=123;// ОШИБКА: (props неизменяемые)this.state.baz=456;// ОШИБКА: (следует использовать this.setState)}}
Это замечательно, если вы хотите использовать нативные массивы JavaScript в иммутабельном виде. На самом деле TypeScript поставляется с интерфейсом ReadonlyArray<T>, позволяющим вам сделать именно это:
12345
letfoo:ReadonlyArray<number>=[1,2,3];console.log(foo[0]);// Okayfoo.push(4);// Ошибка: не возможен в ReadonlyArray, поскольку он изменяет// массивfoo=foo.concat([4]);// Okay: создать копию
В некоторых случаях компилятор может автоматически делать предположение, что определенный элемент доступен только для чтения, например, внутри класса, если у вас есть свойство, у которого есть только геттер, но нет сеттера, оно предполагается только для чтения, например:
1 2 3 4 5 6 7 8 91011
classPerson{firstName:string='John';lastName:string='Doe';getfullName(){returnthis.firstName+this.lastName;}}constperson=newPerson();console.log(person.fullName);// John Doeperson.fullName='Dear Reader';// Ошибка! fullName только для чтения
переменная не может быть переназначена ни на что другое
readonly это
для свойства
свойство может быть изменено из-за ссылочности
Пример, объясняющий 1:
1234
constfoo=123;// ссылка на переменнуюvarbar:{readonlybar:number;// для свойства};
Пример, объясняющий 2:
1 2 3 4 5 6 7 8 9101112
letfoo:{readonlybar:number;}={bar:123,};functioniMutateFoo(foo:{bar:number}){foo.bar=456;}iMutateFoo(foo);// Параметр функции - foo ссылается на переменную fooconsole.log(foo.bar);// 456!
По сути, readonly гарантирует, что свойство не может быть изменено мной, но если вы дадите его кому-то, кто не даёт такой гарантии (кому разрешено по причинам совместимости типов), они могут его изменить. Но если сам iMutateFoo сказал, что они не изменяют foo.bar, компилятор правильно пометит его как ошибку, как показано ниже:
1 2 3 4 5 6 7 8 9101112
interfaceFoo{readonlybar:number;}letfoo:Foo={bar:123,};functioniTakeFoo(foo:Foo){foo.bar=456;// Ошибка! bar только для чтения}iTakeFoo(foo);// Параметр функции - foo ссылается на переменную foo