TypeScript добавляет к классам три модификатора, определяющих откуда можно обращаться к полю или методу:
class Person {
public name: string // доступен везде (по умолчанию)
private age: number // только внутри класса Person
protected email: string // внутри Person и его подклассов
constructor(name: string, age: number, email: string) {
this.name = name
this.age = age
this.email = email
}
getInfo(): string {
return `${this.name}, ${this.age} лет` // private доступен здесь
}
}
const p = new Person('Алексей', 30, 'a@mail.ru')
console.log(p.name) // OK — public
// p.age // Ошибка TS: 'age' is private
// p.email // Ошибка TS: 'email' is protected
class Employee extends Person {
getEmail() {
return this.email // OK — protected доступен в подклассе
// this.age // Ошибка TS: 'age' is private (не в подклассе!)
}
}Модификаторы в конструкторе объявляют и инициализируют поле одновременно:
// Длинная запись
class UserLong {
private name: string
readonly id: number
constructor(name: string, id: number) {
this.name = name
this.id = id
}
}
// Краткая запись — идентична по результату
class UserShort {
constructor(
private name: string,
public readonly id: number,
protected role: string = 'user'
) {}
}TypeScript поддерживает два вида приватности:
class Counter {
private tsPrivate = 0 // TypeScript private — проверяется только компилятором
#jsPrivate = 0 // ECMAScript private — реально приватный в runtime
increment() {
this.tsPrivate++
this.#jsPrivate++
}
}
const c = new Counter()
// c.tsPrivate // Ошибка TS, но в скомпилированном JS доступно!
// c.#jsPrivate // Ошибка и в TS, и в JS runtime — настоящая приватностьИспользуйте # если нужна гарантия приватности в runtime (не только в TypeScript).
Поле можно установить только в объявлении или конструкторе:
class Config {
readonly maxRetries: number
readonly apiUrl: string = 'https://api.example.com'
constructor(maxRetries: number) {
this.maxRetries = maxRetries // OK — в конструкторе
}
update() {
// this.maxRetries = 5 // Ошибка TS: Cannot assign to 'maxRetries'
}
}class Stack<T> {
private items: T[] = []
push(item: T): this { // возвращает this для цепочки
this.items.push(item)
return this
}
pop(): T | undefined {
return this.items.pop()
}
get size(): number { // только чтение снаружи
return this.items.length
}
get isEmpty(): boolean {
return this.items.length === 0
}
protected peek(): T | undefined { // доступен в подклассах
return this.items[this.items.length - 1]
}
}
class BoundedStack<T> extends Stack<T> {
constructor(private readonly capacity: number) {
super()
}
push(item: T): this {
if (this.size >= this.capacity) {
throw new Error(`Стек заполнен (max: ${this.capacity})`)
}
return super.push(item)
}
top(): T | undefined {
return this.peek() // OK — protected
}
}Инкапсуляция через # private fields: банковский счёт с приватным балансом и историей транзакций
// TypeScript private → JS # private fields (ES2022)
// Используем настоящие приватные поля # — реальная инкапсуляция в runtime
class Transaction {
#type // 'deposit' | 'withdrawal'
#amount
#timestamp
#balanceAfter
constructor(type, amount, balanceAfter) {
this.#type = type
this.#amount = amount
this.#timestamp = new Date()
this.#balanceAfter = balanceAfter
}
// Только геттеры — readonly снаружи
get type() { return this.#type }
get amount() { return this.#amount }
get timestamp() { return this.#timestamp }
get balanceAfter() { return this.#balanceAfter }
toString() {
const sign = this.#type === 'deposit' ? '+' : '-'
const time = this.#timestamp.toTimeString().slice(0, 8)
return `[${time}] ${sign}${this.#amount} → баланс: ${this.#balanceAfter}`
}
}
class BankAccount {
#balance
#owner
#history // private — снаружи недоступна
#frozen // private — управление только через методы
// TypeScript: constructor(private owner: string, initialBalance = 0)
constructor(owner, initialBalance = 0) {
if (initialBalance < 0) throw new RangeError('Начальный баланс не может быть отрицательным')
this.#owner = owner
this.#balance = initialBalance
this.#history = []
this.#frozen = false
}
// public геттеры — только чтение
get owner() { return this.#owner }
get balance() { return this.#balance }
get isFrozen() { return this.#frozen }
deposit(amount) {
this.#assertNotFrozen()
this.#assertPositive(amount, 'Сумма пополнения')
this.#balance += amount
this.#history.push(new Transaction('deposit', amount, this.#balance))
return this
}
withdraw(amount) {
this.#assertNotFrozen()
this.#assertPositive(amount, 'Сумма снятия')
if (amount > this.#balance) throw new Error('Недостаточно средств')
this.#balance -= amount
this.#history.push(new Transaction('withdrawal', amount, this.#balance))
return this
}
freeze() { this.#frozen = true; return this }
unfreeze() { this.#frozen = false; return this }
// protected аналог — только для текущего класса
getHistory() {
return [...this.#history] // копия — не мутируем
}
getStatement() {
return [
`Владелец: ${this.#owner}`,
`Баланс: ${this.#balance} руб.`,
`Транзакций: ${this.#history.length}`,
'',
...this.#history.map(t => t.toString()),
].join('\n')
}
// Приватные вспомогательные методы
#assertNotFrozen() {
if (this.#frozen) throw new Error('Счёт заморожен')
}
#assertPositive(value, label) {
if (value <= 0) throw new RangeError(`${label} должна быть положительной`)
}
}
// --- Демонстрация ---
const acc = new BankAccount('Алексей Петров', 10000)
acc.deposit(5000).deposit(3000).withdraw(2000)
console.log('=== История операций ===')
console.log(acc.getStatement())
console.log('\n=== Приватность ===')
// acc.#balance // SyntaxError — реально приватное
// acc.#history // SyntaxError — реально приватное
console.log('Баланс через геттер:', acc.balance) // OK
console.log('\n=== Заморозка счёта ===')
acc.freeze()
console.log('Счёт заморожен:', acc.isFrozen)
try {
acc.withdraw(100)
} catch (e) {
console.log('Ошибка:', e.message) // 'Счёт заморожен'
}
acc.unfreeze()
acc.withdraw(500)
console.log('Баланс после разморозки:', acc.balance)TypeScript добавляет к классам три модификатора, определяющих откуда можно обращаться к полю или методу:
class Person {
public name: string // доступен везде (по умолчанию)
private age: number // только внутри класса Person
protected email: string // внутри Person и его подклассов
constructor(name: string, age: number, email: string) {
this.name = name
this.age = age
this.email = email
}
getInfo(): string {
return `${this.name}, ${this.age} лет` // private доступен здесь
}
}
const p = new Person('Алексей', 30, 'a@mail.ru')
console.log(p.name) // OK — public
// p.age // Ошибка TS: 'age' is private
// p.email // Ошибка TS: 'email' is protected
class Employee extends Person {
getEmail() {
return this.email // OK — protected доступен в подклассе
// this.age // Ошибка TS: 'age' is private (не в подклассе!)
}
}Модификаторы в конструкторе объявляют и инициализируют поле одновременно:
// Длинная запись
class UserLong {
private name: string
readonly id: number
constructor(name: string, id: number) {
this.name = name
this.id = id
}
}
// Краткая запись — идентична по результату
class UserShort {
constructor(
private name: string,
public readonly id: number,
protected role: string = 'user'
) {}
}TypeScript поддерживает два вида приватности:
class Counter {
private tsPrivate = 0 // TypeScript private — проверяется только компилятором
#jsPrivate = 0 // ECMAScript private — реально приватный в runtime
increment() {
this.tsPrivate++
this.#jsPrivate++
}
}
const c = new Counter()
// c.tsPrivate // Ошибка TS, но в скомпилированном JS доступно!
// c.#jsPrivate // Ошибка и в TS, и в JS runtime — настоящая приватностьИспользуйте # если нужна гарантия приватности в runtime (не только в TypeScript).
Поле можно установить только в объявлении или конструкторе:
class Config {
readonly maxRetries: number
readonly apiUrl: string = 'https://api.example.com'
constructor(maxRetries: number) {
this.maxRetries = maxRetries // OK — в конструкторе
}
update() {
// this.maxRetries = 5 // Ошибка TS: Cannot assign to 'maxRetries'
}
}class Stack<T> {
private items: T[] = []
push(item: T): this { // возвращает this для цепочки
this.items.push(item)
return this
}
pop(): T | undefined {
return this.items.pop()
}
get size(): number { // только чтение снаружи
return this.items.length
}
get isEmpty(): boolean {
return this.items.length === 0
}
protected peek(): T | undefined { // доступен в подклассах
return this.items[this.items.length - 1]
}
}
class BoundedStack<T> extends Stack<T> {
constructor(private readonly capacity: number) {
super()
}
push(item: T): this {
if (this.size >= this.capacity) {
throw new Error(`Стек заполнен (max: ${this.capacity})`)
}
return super.push(item)
}
top(): T | undefined {
return this.peek() // OK — protected
}
}Инкапсуляция через # private fields: банковский счёт с приватным балансом и историей транзакций
// TypeScript private → JS # private fields (ES2022)
// Используем настоящие приватные поля # — реальная инкапсуляция в runtime
class Transaction {
#type // 'deposit' | 'withdrawal'
#amount
#timestamp
#balanceAfter
constructor(type, amount, balanceAfter) {
this.#type = type
this.#amount = amount
this.#timestamp = new Date()
this.#balanceAfter = balanceAfter
}
// Только геттеры — readonly снаружи
get type() { return this.#type }
get amount() { return this.#amount }
get timestamp() { return this.#timestamp }
get balanceAfter() { return this.#balanceAfter }
toString() {
const sign = this.#type === 'deposit' ? '+' : '-'
const time = this.#timestamp.toTimeString().slice(0, 8)
return `[${time}] ${sign}${this.#amount} → баланс: ${this.#balanceAfter}`
}
}
class BankAccount {
#balance
#owner
#history // private — снаружи недоступна
#frozen // private — управление только через методы
// TypeScript: constructor(private owner: string, initialBalance = 0)
constructor(owner, initialBalance = 0) {
if (initialBalance < 0) throw new RangeError('Начальный баланс не может быть отрицательным')
this.#owner = owner
this.#balance = initialBalance
this.#history = []
this.#frozen = false
}
// public геттеры — только чтение
get owner() { return this.#owner }
get balance() { return this.#balance }
get isFrozen() { return this.#frozen }
deposit(amount) {
this.#assertNotFrozen()
this.#assertPositive(amount, 'Сумма пополнения')
this.#balance += amount
this.#history.push(new Transaction('deposit', amount, this.#balance))
return this
}
withdraw(amount) {
this.#assertNotFrozen()
this.#assertPositive(amount, 'Сумма снятия')
if (amount > this.#balance) throw new Error('Недостаточно средств')
this.#balance -= amount
this.#history.push(new Transaction('withdrawal', amount, this.#balance))
return this
}
freeze() { this.#frozen = true; return this }
unfreeze() { this.#frozen = false; return this }
// protected аналог — только для текущего класса
getHistory() {
return [...this.#history] // копия — не мутируем
}
getStatement() {
return [
`Владелец: ${this.#owner}`,
`Баланс: ${this.#balance} руб.`,
`Транзакций: ${this.#history.length}`,
'',
...this.#history.map(t => t.toString()),
].join('\n')
}
// Приватные вспомогательные методы
#assertNotFrozen() {
if (this.#frozen) throw new Error('Счёт заморожен')
}
#assertPositive(value, label) {
if (value <= 0) throw new RangeError(`${label} должна быть положительной`)
}
}
// --- Демонстрация ---
const acc = new BankAccount('Алексей Петров', 10000)
acc.deposit(5000).deposit(3000).withdraw(2000)
console.log('=== История операций ===')
console.log(acc.getStatement())
console.log('\n=== Приватность ===')
// acc.#balance // SyntaxError — реально приватное
// acc.#history // SyntaxError — реально приватное
console.log('Баланс через геттер:', acc.balance) // OK
console.log('\n=== Заморозка счёта ===')
acc.freeze()
console.log('Счёт заморожен:', acc.isFrozen)
try {
acc.withdraw(100)
} catch (e) {
console.log('Ошибка:', e.message) // 'Счёт заморожен'
}
acc.unfreeze()
acc.withdraw(500)
console.log('Баланс после разморозки:', acc.balance)Реализуй класс `Temperature` с приватным полем `#celsius`. Конструктор принимает температуру в Цельсиях. Добавь геттеры `celsius`, `fahrenheit` (формула: C * 9/5 + 32), `kelvin` (C + 273.15). Добавь сеттер `celsius` с проверкой: если значение ниже -273.15 — бросай RangeError. Добавь статический метод `fromFahrenheit(f)` — создаёт экземпляр из температуры по Фаренгейту.
В конструкторе и сеттере: if (value < -273.15) throw new RangeError("Ниже абсолютного нуля"). Геттер fahrenheit: return this.#celsius * 9/5 + 32. Геттер kelvin: return this.#celsius + 273.15. fromFahrenheit: return new Temperature((f - 32) * 5/9).
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке