Другое

Геттеры и сеттеры в TypeScript: Полное руководство

Полное руководство по геттерам и сеттерам в TypeScript. Изучите синтаксис, валидацию и лучшие практики для доступа к свойствам в классах.

Как реализовать геттеры и сеттеры в TypeScript?

Я пытаюсь создать методы‑геттеры и сеттеры для свойства в TypeScript, но у меня возникают проблемы с синтаксисом. Вот что я пробовал:

typescript
private _name: string;

Name() {
    get:
    {
        return this._name;
    }
    set:
    {
        this._name = ???;
    }
}

Какой правильный синтаксис для реализации геттеров и сеттеров в TypeScript? Какой ключевое слово нужно использовать, чтобы задать значение в сеттере?

ТипScript‑геттеры и сеттеры используют ключевые слова get и set с определённым синтаксисом, который отличается от того, что вы пытались применить. В TypeScript сеттеры автоматически получают значение как параметр, поэтому специального ключевого слова для установки значения не требуется – вы просто используете переданный параметр.

Содержание

Базовый синтаксис геттеров и сеттеров

Правильный синтаксис геттеров и сеттеров в TypeScript включает использование ключевых слов get и set перед именами свойств. Вот фундаментальная структура:

typescript
class MyClass {
    private _propertyName: DataType;
    
    // Метод геттера
    get propertyName(): DataType {
        return this._propertyName;
    }
    
    // Метод сеттера
    set propertyName(value: DataType) {
        this._propertyName = value;
    }
}

Ключевые моменты этого синтаксиса:

  • Используйте ключевое слово get перед именем свойства для методов геттеров
  • Используйте ключевое слово set перед именем свойства для методов сеттеров
  • Метод сеттера автоматически получает присвоенное значение как параметр, обычно называемый value
  • Имя свойства в геттере/сеттере должно совпадать с публичным интерфейсом, который вы хотите открыть

Полные рабочие примеры

Простой класс Person

Согласно TypeScriptTutorial.net, вот полностью рабочий пример:

typescript
class Person {
    private _age: number;
    private _firstName: string;
    private _lastName: string;

    constructor(age: number, firstName: string, lastName: string) {
        this._age = age;
        this._firstName = firstName;
        this._lastName = lastName;
    }

    // Геттер для возраста
    public get age(): number {
        return this._age;
    }

    // Сеттер для возраста с валидацией
    public set age(theAge: number) {
        if (theAge <= 0 || theAge >= 200) {
            throw new Error('The age is invalid');
        }
        this._age = theAge;
    }

    // Геттер для полного имени
    public get fullName(): string {
        return `${this._firstName} ${this._lastName}`;
    }

    // Сеттер для имени
    public set firstName(theFirstName: string) {
        if (!theFirstName) {
            throw new Error('Invalid first name.');
        }
        this._firstName = theFirstName;
    }
}

// Использование
const person = new Person(25, 'John', 'Doe');
console.log(person.age); // 25 (вызывается геттер)
console.log(person.fullName); // John Doe (вызывается геттер)

person.age = 30; // вызывается сеттер
person.firstName = 'Jane'; // вызывается сеттер

Класс Employee с валидацией

Согласно Tektutorialshub, вот как реализовать геттеры и сеттеры с валидацией:

typescript
class Employee {
    private _emp_Name: string;
    private _age_of_emp: number;
    private _role: string;

    constructor(name: string, age: number, role: string) {
        this._emp_Name = name;
        this._age_of_emp = age;
        this._role = role;
    }

    // Геттер для имени
    get name(): string {
        return this._emp_Name;
    }

    // Сеттер для имени с валидацией
    set name(newName: string) {
        if (newName.length < 2) {
            throw new Error('Name must be at least 2 characters long');
        }
        this._emp_Name = newName;
    }

    // Геттер для возраста
    get age(): number {
        return this._age_of_emp;
    }

    // Сеттер для возраста с валидацией
    set age(newAge: number) {
        if (newAge < 18 || newAge > 65) {
            throw new Error('Age must be between 18 and 65');
        }
        this._age_of_emp = newAge;
    }
}

// Использование
const emp = new Employee('Alice', 30, 'Developer');
console.log(emp.name); // Alice (вызывается геттер)

emp.name = 'Bob'; // сеттер с валидацией
emp.age = 32; // сеттер с валидацией

Пример вычисляемого свойства

Согласно GeeksforGeeks, вот пример с вычисляемым свойством:

typescript
class User {
    private _firstName: string;
    private _lastName: string;

    constructor(firstName: string, lastName: string) {
        this._firstName = firstName;
        this._lastName = lastName;
    }

    // Геттер для полного имени (вычисляемое свойство)
    get fullName(): string {
        return `${this._firstName} ${this._lastName}`;
    }

    // Сеттер для фамилии
    set lastName(newLastName: string) {
        if (!newLastName || newLastName.trim() === '') {
            throw new Error('Last name cannot be empty');
        }
        this._lastName = newLastName;
    }
}

// Использование
const user = new User('John', 'Smith');
console.log(user.fullName); // John Smith (вызывается геттер)

user.lastName = 'Doe'; // сеттер
console.log(user.fullName); // John Doe (обновлённый геттер)

Распространённые ошибки и решения

Ошибка 1: Неправильная структура синтаксиса

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

typescript
// ❌ НЕПРАВИЛЬНО
Name() {
    get:
    {
        return this._name;
    }
    set:
    {
        this._name = ???;
    }
}

Решение: Используйте отдельные методы get и set:

typescript
// ✅ ПРАВИЛЬНО
get name(): string {
    return this._name;
}

set name(value: string) {
    this._name = value;
}

Ошибка 2: Смешение имён свойств

Частая путаница возникает с именованием свойств и параметра в сеттерах.

Решение: Помните, что:

  • Имя метода сеттера совпадает с публичным именем свойства
  • Параметр сеттера автоматически называется value (можно переименовать, но value является общепринятым)
typescript
// ✅ Использование стандартного имени параметра 'value'
set name(value: string) {
    this._name = value;
}

// ✅ Переименование параметра (менее привычно)
set name(newName: string) {
    this._name = newName;
}

Ошибка 3: Отсутствие типа параметра в сеттере

Забывание указать тип параметра в сеттере может привести к проблемам типобезопасности.

Решение: Всегда указывайте тип параметра:

typescript
// ❌ Отсутствует тип параметра
set name(value) {
    this._name = value;
}

// ✅ Правильно с типом параметра
set name(value: string) {
    this._name = value;
}

Ошибка 4: Неправильный тип возвращаемого значения в геттере

Забывание указать тип возвращаемого значения в геттере.

Решение: Всегда указывайте тип возвращаемого значения:

typescript
// ❌ Отсутствует тип возвращаемого значения
get name() {
    return this._name;
}

// ✅ Правильно с типом возвращаемого значения
get name(): string {
    return this._name;
}

Лучшие практики

1. Используйте приватные свойства с префиксом подчёркивания

Как рекомендует TypeScriptTutorial.net, используйте приватное свойство с префиксом подчёркивания и открывайте его через геттеры/сеттеры:

typescript
class MyClass {
    private _propertyName: string; // Приватное поле
    
    get propertyName(): string {
        return this._propertyName;
    }
    
    set propertyName(value: string) {
        this._propertyName = value;
    }
}

2. Добавляйте валидацию в сеттерах

Используйте сеттеры для добавления логики валидации, как показано в примерах из Tektutorialshub:

typescript
set age(value: number) {
    if (value < 0) {
        throw new Error('Age cannot be negative');
    }
    this._age = value;
}

3. Используйте только геттеры для чтения

Для свойств, которые должны быть только читаемыми, реализуйте только геттер:

typescript
readonly get fullName(): string {
    return `${this._firstName} ${this._lastName}`;
}

4. Рассмотрите вычисляемые свойства

Создавайте вычисляемые свойства, которые зависят от других свойств, как в примере из GeeksforGeeks:

typescript
get fullName(): string {
    return `${this._firstName} ${this._lastName}`;
}

5. Соблюдайте соглашения об именовании

Как отмечено в Stack Overflow, придерживайтесь последовательных соглашений:

  • Используйте PascalCase для имён свойств геттеров/сеттеров
  • Используйте camelCase для приватных полей с префиксом подчёркивания
  • Держите имена геттеров/сеттеров простыми и описательными

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

Доступ к свойству с побочными эффектами

Геттеры и сеттеры могут вызывать побочные эффекты при доступе или изменении свойств:

typescript
class TemperatureMonitor {
    private _currentTemp: number = 20;
    private _alertCount: number = 0;

    get temperature(): number {
        console.log(`Temperature accessed: ${this._currentTemp}°C`);
        return this._currentTemp;
    }

    set temperature(value: number) {
        console.log(`Temperature set to: ${value}°C`);
        if (value > 30) {
            this._alertCount++;
            console.warn(`High temperature alert! Total alerts: ${this._alertCount}`);
        }
        this._currentTemp = value;
    }
}

Условные геттеры/сеттеры

Можно условно реализовать геттеры и сеттеры в зависимости от определённых условий:

typescript
class Config {
    private _apiKey: string;
    private _isReadOnly: boolean = false;

    get apiKey(): string {
        if (this._isReadOnly) {
            console.warn('Config is read-only');
        }
        return this._apiKey;
    }

    set apiKey(value: string) {
        if (this._isReadOnly) {
            throw new Error('Cannot modify read-only configuration');
        }
        this._apiKey = value;
    }

    public makeReadOnly(): void {
        this._isReadOnly = true;
    }
}

Использование геттеров/сеттеров с интерфейсами

Определите интерфейсы, которые задают контракт для геттеров и сеттеров:

typescript
interface IUser {
    get name(): string;
    set name(value: string);
    get email(): string;
}

class User implements IUser {
    private _name: string;
    private _email: string;

    constructor(name: string, email: string) {
        this._name = name;
        this._email = email;
    }

    get name(): string {
        return this._name;
    }

    set name(value: string) {
        this._name = value;
    }

    get email(): string {
        return this._email;
    }
}

Заключение

Реализация геттеров и сеттеров в TypeScript проста, когда вы понимаете правильный синтаксис:

  1. Используйте ключевые слова get и set перед именем свойства
  2. Указывайте типы параметров и возвращаемых значений для типобезопасности
  3. Используйте приватные поля с префиксом подчёркивания для инкапсуляции внутреннего состояния
  4. Добавляйте логику валидации в сеттерах, чтобы обеспечить целостность данных
  5. Рассмотрите вычисляемые свойства, которые зависят от других полей

Ключевое отличие от вашего первоначального подхода в том, что TypeScript автоматически передаёт значение параметру сеттера, поэтому специального ключевого слова для установки значений не требуется – вы просто используете параметр value (или любое другое имя, которое выберете).

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

Авторы
Проверено модерацией
Модерация