Что такое get и set (геттеры и сеттеры) в TypeScript и как их правильно использовать?
Правильное объявление геттеров и сеттеров
Основная проблема вашего кода в том, что вы назвали приватное поле и геттер/сеттер одинаково — name. В TypeScript это вызывает конфликт. Вам нужно использовать разные имена. Обычно приватное поле называют с нижним подчеркиванием в начале:
class User {
private _name: string;
constructor(name: string) {
this._name = name;
}
get name(): string {
return this._name;
}
set name(value: string) {
if (value.length > 0) {
this._name = value;
} else {
console.error('Имя не может быть пустым');
}
}
}
const user = new User('Alice');
console.log(user.name); // Alice
user.name = 'Bob';
console.log(user.name); // Bob
user.name = ''; // Имя не может быть пустым
Зачем нужны геттеры и сеттеры?
Главное преимущество — контроль доступа и валидация. Вот несколько практических сценариев:
- Валидация данных — как в примере выше, можно проверять, что имя не пустое
- Вычисляемые свойства — геттер может возвращать производное значение
- Логирование — можно логировать каждое обращение к свойству
- Ленивая инициализация — вычислять значение только при первом обращении
- Совместимость с API — можно менять внутреннюю структуру, не ломая внешний интерфейс
Практический пример с вычисляемым свойством
class Rectangle {
private _width: number;
private _height: number;
constructor(width: number, height: number) {
this._width = width;
this._height = height;
}
get width(): number {
return this._width;
}
set width(value: number) {
if (value > 0) {
this._width = value;
}
}
get height(): number {
return this._height;
}
set height(value: number) {
if (value > 0) {
this._height = value;
}
}
// Вычисляемое свойство — не хранится, а вычисляется на лету
get area(): number {
return this._width * this._height;
}
}
const rect = new Rectangle(10, 20);
console.log(rect.area); // 200
rect.width = 15;
console.log(rect.area); // 300
Когда использовать геттеры/сеттеры, а когда публичные поля?
Используйте геттеры/сеттеры, если:
- Нужна валидация при установке значения
- Свойство должно быть вычисляемым (например,
fullNameизfirstNameиlastName) - Вы планируете изменить внутреннюю реализацию в будущем
- Нужна дополнительная логика (логирование, триггеры событий)
Используйте публичные поля, если:
- Свойство простое и не требует валидации
- Это DTO (Data Transfer Object) — простой контейнер данных
- Производительность критична (геттеры/сеттеры имеют небольшой оверхед)
Современный синтаксис с public и readonly
class User {
// Простое публичное поле — без геттеров/сеттеров
public readonly createdAt: Date = new Date();
// Приватное поле с геттером/сеттером
private _age: number;
constructor(private _name: string, age: number) {
this._age = age;
}
get name(): string {
return this._name;
}
set name(value: string) {
if (value.length > 0) {
this._name = value;
}
}
get age(): number {
return this._age;
}
}
Главное правило: геттеры и сеттеры — это инструмент для инкапсуляции. Они позволяют контролировать доступ к внутренним данным и добавлять логику без изменения внешнего API класса.
<<Альтернативный подход: использование Proxy
В некоторых случаях, особенно при работе с динамическими объектами, можно использовать Proxy вместо геттеров/сеттеров в классе. Это позволяет перехватывать обращения к любым свойствам объекта:
function createValidatedObject(target: Record<string, any>) {
return new Proxy(target, {
set(obj, prop, value) {
if (prop === 'name' && typeof value !== 'string') {
throw new Error('Имя должно быть строкой');
}
if (prop === 'age' && (typeof value !== 'number' || value < 0)) {
throw new Error('Возраст должен быть положительным числом');
}
obj[prop] = value;
return true;
},
get(obj, prop) {
console.log(`Доступ к свойству ${String(prop)}`);
return obj[prop];
}
});
}
const user = createValidatedObject({ name: 'Alice', age: 25 });
console.log(user.name); // Лог: Доступ к свойству name
user.age = 30; // OK
// user.age = -5; // Ошибка: Возраст должен быть положительным числом
Когда это полезно:
- Когда у вас много свойств и не хочется писать геттер/сеттер для каждого
- Когда структура объекта динамическая (свойства добавляются на лету)
- Когда нужна универсальная логика для всех свойств
Недостатки:
- Нет строгой типизации — TypeScript не может проверить типы через Proxy
- Производительность немного ниже, чем у нативных геттеров/сеттеров
- Сложнее отлаживать
В большинстве случаев для классов лучше использовать стандартные геттеры/сеттеры TypeScript — они более производительны, типизированы и понятны другим разработчикам. Proxy стоит применять только в специфических задачах, например, при создании ORM или валидаторов.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги