Перейти к содержимому
Теория · Классы · урок 5 из 12

readonly и параметры-свойства

Модификатор readonly вы уже видели у свойств объектного типа. У поля класса он работает так же. В картинке с чертежом это номер дома на фасаде. Его прикрепляют, пока дом строят, и потом не перевешивают, иначе почта понесёт письма не туда.

TypeScript
class Order {
    readonly id: number;
    total = 0;

    constructor(id: number) {
        this.id = id;
    }
}

const order = new Order(7);
order.total = 150;
console.log(order.id, order.total); // 7 150

Разберём пример по строкам:

  1. readonly id: number объявляет поле только для чтения.
  2. Конструктор записывает в него id, и это разрешено: номер вешают, пока дом строится.
  3. order.total = 150 меняет обычное поле, и компилятор не возражает.
  4. Программа печатает 7 150.

Запись в readonly-поле

Записать readonly-поле можно только в двух местах: в его объявлении и в конструкторе. Любая запись после этого — ошибка, будь то order.id = 8 снаружи или this.id = next в методе самого класса:

TEXT
error TS2540: Cannot assign to 'id' because it is a read-only property.

«Нельзя присвоить id, потому что это свойство только для чтения». Этот запрет проверяет только tsc. В JavaScript id останется обычным полем, и при запуске запись сработает.

Параметры-свойства

Посмотрите на класс Order ещё раз: имя id в нём написано трижды. Сначала объявление поля, потом параметр конструктора, потом присваивание this.id = id. Чем больше полей, тем больше такого повторения.

В TypeScript для этого есть сокращение. Параметр-свойство — это параметр конструктора с модификатором public, private, protected или readonly. Такой параметр сразу объявляет поле с тем же именем и кладёт в него аргумент:

TypeScript
class User {
    constructor(private readonly id: number, public login: string) {}

    describe(): string {
        return `${this.id}: ${this.login}`;
    }
}

const user = new User(7, "anna");
console.log(user.describe(), user.login); // 7: anna anna

Разберём конструктор по частям:

  • private readonly id: number объявляет закрытое поле id только для чтения;
  • public login: string объявляет открытое поле login, и слово public здесь обязательно: без модификатора параметр остался бы обычным параметром;
  • тело конструктора пустое: присваивания tsc напишет сам.

Вот JavaScript, который tsc пишет из этого файла:

JavaScript
"use strict";
class User {
    id;
    login;
    constructor(id, login) {
        this.id = id;
        this.login = login;
    }
    describe() {
        return `${this.id}: ${this.login}`;
    }
}
const user = new User(7, "anna");
console.log(user.describe(), user.login); // 7: anna anna

В коде появились объявления полей id и login и два присваивания в конструкторе. Их не было в файле .ts: tsc дописал их сам.

На собеседовании о параметрах-свойствах спрашивают так: что создаёт constructor(private name: string) {}? Ответ: закрытое поле name и присваивание ему аргумента.

node file.ts их не запустит

Обычно, чтобы получить JavaScript, из файла достаточно вырезать типы. С параметрами-свойствами так не выйдет: нужно дописать новые строки. Поэтому node user.ts, который умеет только вырезать типы, отказывается запускать такой файл:

TEXT
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript parameter property is not supported in strip-only mode

Ошибки в коде нет, и tsc его компилирует. Скомпилируйте файл через tsc user.ts и запустите node user.js или проверьте код в TypeScript Playground.

Если проект запускают через node file.ts, параметры-свойства в нём не пишут: поля объявляют и присваивают, как в классе Order.

Частая ошибка

Модификатор у параметра забыли:

TypeScript
class User {
    constructor(login: string) {}

    describe(): string {
        return this.login; // ✗
    }
}
TEXT
error TS2339: Property 'login' does not exist on type 'User'.

Без модификатора login — обычный параметр: он живёт только внутри конструктора, и поля из него не получается. Допишите модификатор: constructor(public login: string) {}.

Главное

  • readonly-поле записывают в объявлении или в конструкторе, а потом только читают: иначе будет ошибка TS2540.
  • Параметр конструктора с public, private, protected или readonly становится полем, и присваивание tsc пишет сам.
  • Параметр без модификатора остаётся обычным параметром и полем не становится.
  • node file.ts не запускает файл с параметрами-свойствами: нужен tsc или Playground.