get, set и static
На стене дома по нашему чертежу висит термостат. На табло видно число, а ручкой его меняют. Но за табло работает механизм: ручка не повернётся ниже допустимого, а табло может показать температуру и в других единицах. Методы get и set из JavaScript устроены так же: снаружи это обычное свойство, а при чтении и записи работает код.
TypeScript добавляет к ним типы:
class Temperature {
#celsius = 0;
get celsius(): number {
return this.#celsius;
}
set celsius(value: number) {
this.#celsius = Math.max(value, -273.15);
}
}
const t = new Temperature();
t.celsius = -300;
console.log(t.celsius); // -273.15Разберём пример по строкам:
- Поле
#celsiusхранит значение, и снаружи его не видно. get celsius(): number— геттер. Для кода снаружиt.celsiusвыглядит как поле типаnumber.set celsius(value: number)— сеттер. Записьt.celsius = -300вызывает его, и он не пускает значение ниже абсолютного нуля.- Программа печатает
-273.15.
Типы у get и set
У геттера указывают тип результата, у сеттера — тип параметра. tsc проверяет по ним каждое чтение и каждую запись. Например, t.celsius = "20" не пройдёт: сеттер ждёт число.
error TS2322: Type 'string' is not assignable to type 'number'.Если у параметра сеттера не написать тип, TypeScript возьмёт его из результата геттера с тем же именем.
Здесь аналогия с термостатом перестаёт работать. Табло показывает число, которое где-то хранится, а геттер ничего не хранит: он заново выполняет свой код при каждом чтении.
Только get
Свойство с геттером, но без сеттера, для tsc — свойство только для чтения:
class Circle {
radius = 1;
get area(): number {
return Math.PI * this.radius ** 2;
}
}
const c = new Circle();
c.area = 10; // ✗error TS2540: Cannot assign to 'area' because it is a read-only property.Ошибка та же, что при записи в поле readonly. А что будет при запуске, зависит от способа. В JavaScript, который пишет tsc, первая строка — "use strict";, и программа остановится на строке c.area = 10:
TypeError: Cannot set property area of #<Circle> which has only a getternode file.ts строку "use strict"; не добавляет, и там запись пропадёт молча: c.area по-прежнему вернёт площадь круга.
static-члены
Теперь второй, отдельный инструмент урока: с get и set он не связан. Вернёмся к чертежу. На нём самом, а не на каком-то доме, можно вести запись: «по этому чертежу построено 12 домов». Такая запись одна на весь чертёж. Статический член класса устроен так же: он принадлежит самому классу, и он один, сколько бы объектов ни создали.
class Ticket {
static issued = 0;
id: number;
constructor() {
Ticket.issued += 1;
this.id = Ticket.issued;
}
}
const a = new Ticket();
const b = new Ticket();
console.log(a.id, b.id, Ticket.issued); // 1 2 2static issued = 0— одно поле на весь класс, его типnumberвыведен из значения.- Конструктор увеличивает общий счётчик через имя класса:
Ticket.issued += 1. - Обычное поле
idу каждого билета своё: оно получает текущее значение счётчика. - Программа печатает
1 2 2: у билетов номера1и2, а счётчик один, и в нём2.
Методы тоже бывают статическими. Внутри static-метода this — это сам класс, поэтому static reset(): void { this.issued = 0; } обнулит общий счётчик.
На собеседовании часто спрашивают, чем статический член отличается от члена экземпляра. Ответ: статический член один на весь класс, и к нему обращаются через имя класса. Член экземпляра у каждого объекта свой, его создаёт new, а внутри методов к нему обращаются через this.
Частая ошибка
К статическому полю обратились через this, как к обычному:
class Ticket {
static issued = 0;
constructor() {
this.issued += 1; // ✗
}
}error TS2576: Property 'issued' does not exist on type 'Ticket'. Did you mean to access the static member 'Ticket.issued' instead?В конструкторе this — новый билет, а счётчик висит на чертеже, не на доме. Сообщение само подсказывает исправление: пишите Ticket.issued.
Главное
- У
getуказывают тип результата, уset— тип параметра, иtscпроверяет по ним каждое чтение и запись. - Свойство только с
get— только для чтения: запись даёт ошибкуTS2540. static-член один на весь класс, и к нему обращаются через имя класса:Ticket.issued.- Через экземпляр
static-член не виден: ошибкаTS2576; а вstatic-методеthis— сам класс.