Readonly typescript что это

Модификатор readonly¶

Локальную переменную можно определить неизменяемой при помощи ключевого слова const . Но как реализовать подобное поведение для полей класса? На данный вопрос как раз и призвана ответить текущая глава.

Модификатор readonly¶

В TypeScript существует модификатор, с помощью которого поле объекта помечается как “только для чтения”. Модификатор, запрещающий изменение значений полей объектов, указывается с помощью ключевого слова readonly . Он применяется к полям как при их объявлении в классе, так и при их описании в интерфейсе.

interface IAnimal  readonly name: string; > class Animal  public readonly name: string = 'name'; > 

Если при описании интерфейса поле было помечено как readonly , то классам, реализующим этот интерфейс, не обязательно указывать модификатор readonly . Но в таком случае значение поля будет изменяемым.

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
interface IAnimal  readonly name: string; > class Bird implements IAnimal  public readonly name: string = 'bird'; // Ok > class Fish implements IAnimal  public name: string = 'fish'; // Ok > const bird: Bird = new Bird(); bird.name = 'newbird'; // Error const fish: Fish = new Fish(); fish.name = 'newfish'; // Ok 

Это правило работает и в обратном направлении — поле, описанное в интерфейсе без указания модификатора readonly , может быть помечено этим модификатором при реализации.

interface IAnimal  name: string; > class Bird implements IAnimal  public readonly name: string = 'bird'; // Ok > const bird: Bird = new Bird(); bird.name = 'newbird'; // Error 

Модификатор readonly , примененный к параметрам конструктора, заставляет компилятор расценивать их как поля класса.

interface IAnimal  name: string; > class Bird implements IAnimal  constructor(readonly name: string) <> > const bird: Bird = new Bird('bird'); 

Кроме того модификатор readonly , примененный к параметрам конструктора, можно комбинировать с другими модификаторами доступа.

interface IAnimal  name: string; > class Bird implements IAnimal  constructor(private readonly name: string) <> > const bird: Bird = new Bird('bird'); 

Полю, к которому применен модификатор readonly , не обязательно присваивать значение в момент объявления. Но в таком случае присвоить ему значение можно будет только из конструктора класса, в котором это поле объявлено. Если полю был применён модификатор readonly и не было присвоено значение, то такое поле, так же как и любое другое неинициализированное поле, будет иметь значение undefined .

interface IAnimal  readonly name: string; > class Animal implements IAnimal  public readonly name: string; // Ok constructor()  this.name = 'animal'; // Ok > > 

Попытка присвоить значение полю, к которому применен модификатор readonly в месте, отличном от места объявления или конструктора класса, приведет к возникновению ошибки.

interface IAnimal  readonly name: string; > class Animal implements IAnimal  public readonly name: string; // Ok public setName(name: string): void  this.name = name; // Error > > 

Не получится избежать возникновения ошибки и при попытке присвоить значение из конструктора класса-потомка (с условием, что потомок не переопределяет его).

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
interface IAnimal  readonly name: string; readonly age: number; > class Animal implements IAnimal  public readonly name: string; // Ok public readonly age: number; // Ok > class Bird extends Animal  public readonly age: number; // переопределение поля constructor()  super(); super.name = 'bird'; // Error this.age = 0; // Ok > > 

Поле объекта, созданного с помощью литерала объекта, будет невозможно изменить, если в связанном с ним типе к этим полям применен модификатор readonly .

interface IAnimal  readonly name: string; > const animal: IAnimal =  name: 'animal' >; animal.name = 'newanimal'; // Error 

Если полям, помеченным “только для чтения”, не указан тип, а присвоение примитивного значения происходит в месте объявления, то для таких полей вывод типов укажет принадлежность к литеральному типу.

class Animal  public readonly nameReadonly = 'animal'; // nameReadonly: "animal" public nameDefault = 'animal'; // nameDefault: string public readonly ageReadonly = 0; // ageReadonly: 0 public ageDefault = 0; // ageReadonly: number public readonly isLifeReadonly = true; // isLifeReadonly: true public isLifeDefault = true; // isLifeDefault: boolean > 

Источник

readonly¶

Система типов TypeScript позволяет помечать отдельные элементы интерфейса доступными только для чтения . Это позволяет вам работать в функциональном стиле (в котором неожиданные мутации это плохо):

function foo(config:  readonly bar: number; readonly bas: number; >)  // .. > let config =  bar: 123, bas: 123 >; foo(config); // Вы можете быть уверены, что `config` не изменился 🌹 

Вы также можете использовать readonly в определениях interface и type , например:

type Foo =  readonly bar: number; readonly bas: number; >; // Инициализация в порядке let foo: Foo =  bar: 123, bas: 456 >; // Мутация нет foo.bar = 456; // Ошибка: Выражение присваивания не может осуществлено // для константы или свойства, доступного только для чтения 

Вы даже можете объявить свойство класса как readonly . Вы можете инициализировать их в момент объявления или в конструкторе, как показано ниже:

class Foo  readonly bar = 1; // OK readonly baz: string; constructor()  this.baz = 'hello'; // OK > > 

Тип Readonly¶

Существует тип Readonly , который принимает тип T и помечает все его элементы как readonly . Вот демонстрация использования этого на практике:

 1 2 3 4 5 6 7 8 9 10 11 12
type Foo =  bar: number; bas: number; >; type FooReadonly = ReadonlyFoo>; let foo: Foo =  bar: 123, bas: 456 >; let fooReadonly: FooReadonly =  bar: 123, bas: 456 >; foo.bar = 456; // Okay fooReadonly.bar = 456; // ОШИБКА: bar только для чтения 

Различные варианты использования¶

ReactJS¶

ReactJS — это библиотека, которая любит иммутабельность, вы можете пометить ваши Props и State как неизменяемые, например:

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
interface Props  readonly foo: number; > interface State  readonly bar: number; > export class Something extends React.Component Props, State >  someMethod()  // Вы можете быть уверены, что никто не сможет сделать следующее this.props.foo = 123; // ОШИБКА: (props неизменяемые) this.state.baz = 456; // ОШИБКА: (следует использовать this.setState) > > 

Однако вам не нужно это делать, поскольку определения типов для React уже помечают их как readonly (внутренняя оболочка универсального типа соответствует типу Readonly , упомянутому выше). Поэтому, достаточно:

export class Something extends React.Component  foo: number >,  baz: number > >  // Вы можете быть уверены, что никто не сможет сделать следующее someMethod()  this.props.foo = 123; // ОШИБКА: (props неизменяемые) this.state.baz = 456; // ОШИБКА: (следует использовать this.setState) > > 

Обратно-совместимая иммутабельность¶

Вы даже можете пометить сигнатуры индекса только для чтения:

 1 2 3 4 5 6 7 8 9 10 11 12 13
/** * Объявление */ interface Foo  readonly [x: number]: number; > /** * Использование */ let foo: Foo =  0: 123, 2: 345 >; console.log(foo[0]); // Okay (чтение) foo[0] = 456; // Ошибка (изменение): Readonly 

Это замечательно, если вы хотите использовать нативные массивы JavaScript в иммутабельном виде. На самом деле TypeScript поставляется с интерфейсом ReadonlyArray , позволяющим вам сделать именно это:

let foo: ReadonlyArraynumber> = [1, 2, 3]; console.log(foo[0]); // Okay foo.push(4); // Ошибка: не возможен в ReadonlyArray, поскольку он изменяет // массив foo = foo.concat([4]); // Okay: создать копию 

Автоматический логический вывод¶

В некоторых случаях компилятор может автоматически делать предположение, что определенный элемент доступен только для чтения, например, внутри класса, если у вас есть свойство, у которого есть только геттер, но нет сеттера, оно предполагается только для чтения, например:

class Person  firstName: string = 'John'; lastName: string = 'Doe'; get fullName()  return this.firstName + this.lastName; > > const person = new Person(); console.log(person.fullName); // John Doe person.fullName = 'Dear Reader'; // Ошибка! fullName только для чтения 

Отличие от const ¶

const foo = 123; // ссылка на переменную var bar:  readonly bar: number; // для свойства >; 
 1 2 3 4 5 6 7 8 9 10 11 12
let foo:  readonly bar: number; > =  bar: 123, >; function iMutateFoo(foo:  bar: number >)  foo.bar = 456; > iMutateFoo(foo); // Параметр функции - foo ссылается на переменную foo console.log(foo.bar); // 456! 

По сути, readonly гарантирует, что свойство не может быть изменено мной, но если вы дадите его кому-то, кто не даёт такой гарантии (кому разрешено по причинам совместимости типов), они могут его изменить. Но если сам iMutateFoo сказал, что они не изменяют foo.bar , компилятор правильно пометит его как ошибку, как показано ниже:

 1 2 3 4 5 6 7 8 9 10 11 12
interface Foo  readonly bar: number; > let foo: Foo =  bar: 123, >; function iTakeFoo(foo: Foo)  foo.bar = 456; // Ошибка! bar только для чтения > iTakeFoo(foo); // Параметр функции - foo ссылается на переменную foo 

Источник

Utility Types

TypeScript provides several utility types to facilitate common type transformations. These utilities are available globally.

This type is meant to model operations like await in async functions, or the .then() method on Promise s — specifically, the way that they recursively unwrap Promise s.

Constructs a type with all properties of Type set to optional. This utility will return a type that represents all subsets of a given type.

Constructs a type consisting of all properties of Type set to required. The opposite of Partial .

Constructs a type with all properties of Type set to readonly , meaning the properties of the constructed type cannot be reassigned.

This utility is useful for representing assignment expressions that will fail at runtime (i.e. when attempting to reassign properties of a frozen object).

ts
function freezeType>(obj: Type): ReadonlyType>;

Constructs an object type whose property keys are Keys and whose property values are Type . This utility can be used to map the properties of a type to another type.

Constructs a type by picking the set of properties Keys (string literal or union of string literals) from Type .

Constructs a type by picking all properties from Type and then removing Keys (string literal or union of string literals). The opposite of Pick .

Constructs a type by excluding from UnionType all union members that are assignable to ExcludedMembers .

Constructs a type by extracting from Type all union members that are assignable to Union .

Constructs a type by excluding null and undefined from Type .

Constructs a tuple type from the types used in the parameters of a function type Type .

Constructs a tuple or array type from the types of a constructor function type. It produces a tuple type with all the parameter types (or the type never if Type is not a function).

Constructs a type consisting of the return type of function Type .

Constructs a type consisting of the instance type of a constructor function in Type .

Extracts the type of the this parameter for a function type, or unknown if the function type has no this parameter.

Removes the this parameter from Type . If Type has no explicitly declared this parameter, the result is simply Type . Otherwise, a new function type with no this parameter is created from Type . Generics are erased and only the last overload signature is propagated into the new function type.

This utility does not return a transformed type. Instead, it serves as a marker for a contextual this type. Note that the noImplicitThis flag must be enabled to use this utility.

In the example above, the methods object in the argument to makeObject has a contextual type that includes ThisType and therefore the type of this in methods within the methods object is < x: number, y: number >& < moveBy(dx: number, dy: number): void >. Notice how the type of the methods property simultaneously is an inference target and a source for the this type in methods.

The ThisType marker interface is simply an empty interface declared in lib.d.ts . Beyond being recognized in the contextual type of an object literal, the interface acts like any empty interface.

Intrinsic String Manipulation Types

To help with string manipulation around template string literals, TypeScript includes a set of types which can be used in string manipulation within the type system. You can find those in the Template Literal Types documentation.

The TypeScript docs are an open source project. Help us improve these pages by sending a Pull Request ❤

Источник

Читайте также:  Python static property decorator
Оцените статью