readonly запрещает переприсвоение свойства после инициализации. Это TypeScript-конструкция — в скомпилированном JS её нет.
interface Point {
readonly x: number
readonly y: number
}
const point: Point = { x: 10, y: 20 }
point.x = 5 // Ошибка TS: Cannot assign to 'x' because it is a read-only property
point.y = 8 // Ошибка TS: Cannot assign to 'y' because it is a read-only property
// Но создание нового объекта — OK:
const newPoint: Point = { x: point.x + 1, y: point.y }class Circle {
readonly radius: number
constructor(radius: number) {
this.radius = radius // OK — инициализация в конструкторе разрешена
}
grow() {
this.radius = this.radius + 1 // Ошибка: readonly
}
}const nums: ReadonlyArray<number> = [1, 2, 3]
// Или: readonly number[]
nums.push(4) // Ошибка: push не существует на ReadonlyArray
nums[0] = 10 // Ошибка: Index signature in type 'readonly number[]' only permits reading
nums.sort() // Ошибка: sort мутирует массив
// Разрешённые операции:
console.log(nums[0]) // OK — чтение
console.log(nums.length) // OK
const copy = [...nums] // OK — создание нового массиваas const делает значение **полностью неизменяемым** и выводит **литеральные типы** вместо широких типов.
// Без as const — широкие типы:
const config = {
host: 'localhost', // тип: string
port: 3000, // тип: number
debug: true // тип: boolean
}
// С as const — литеральные типы:
const config = {
host: 'localhost', // тип: 'localhost'
port: 3000, // тип: 3000
debug: true // тип: true
} as const
config.host = 'example.com' // Ошибка: readonly// Без as const:
const colors = ['red', 'green', 'blue']
// тип: string[] — потерян порядок и конкретные значения
// С as const:
const colors = ['red', 'green', 'blue'] as const
// тип: readonly ['red', 'green', 'blue'] — кортеж с литеральными типами
type Color = typeof colors[number]
// type Color = 'red' | 'green' | 'blue' — автоматически из массива!// Паттерн: as const для enum-like объектов
const STATUS = {
PENDING: 'pending',
ACTIVE: 'active',
CLOSED: 'closed',
} as const
type Status = typeof STATUS[keyof typeof STATUS]
// type Status = 'pending' | 'active' | 'closed'
function setStatus(status: Status) { /* ... */ }
setStatus(STATUS.ACTIVE) // OK
setStatus('pending') // OK
setStatus('unknown') // Ошибка TS!| | const | readonly |
|---|---|---|
| Применяется к | Переменным | Свойствам объектов |
| Что защищает | Переприсвоение переменной | Переприсвоение свойства |
| Уровень | Runtime (JS) | Compile-time (TS) |
| Глубина | Поверхностная | Поверхностная |
const не делает объект неизменяемым — только запрещает переприсвоение самой переменной. as const делает объект полностью readonly рекурсивно.
Демонстрация readonly-паттернов через Object.freeze и защиту от мутаций
// В JS readonly реализуется через Object.freeze()
// В TS — через модификатор readonly и as const (только compile-time)
// === Паттерн readonly объект (как TS readonly properties) ===
const point = Object.freeze({ x: 10, y: 20 })
console.log('=== Readonly объект ===')
console.log(point.x, point.y) // 10 20
// Попытка изменить — молча проигнорируется в non-strict режиме
point.x = 999 // В TS: Ошибка компиляции. В JS: просто игнорируется
console.log(point.x) // 10 — значение не изменилось
// === Паттерн as const для enum-like констант ===
const STATUS = Object.freeze({
PENDING: 'pending',
ACTIVE: 'active',
CLOSED: 'closed',
})
// В TS с as const: тип STATUS.PENDING === 'pending' (литерал, не string)
console.log('\n=== Enum-like константы ===')
console.log(STATUS.PENDING) // 'pending'
console.log(STATUS.ACTIVE) // 'active'
// Имитация type Status = typeof STATUS[keyof typeof STATUS]
const validStatuses = Object.values(STATUS)
function setStatus(status) {
if (!validStatuses.includes(status)) {
throw new Error(`Недопустимый статус: ${status}. Допустимые: ${validStatuses.join(', ')}`)
}
console.log(`Статус установлен: ${status}`)
}
setStatus(STATUS.ACTIVE) // 'Статус установлен: active'
setStatus('pending') // OK
try {
setStatus('unknown') // В TS: ошибка компиляции. В JS: runtime ошибка
} catch (e) {
console.log(e.message)
}
// === ReadonlyArray паттерн ===
console.log('\n=== Readonly массив ===')
// В TS: const colors: readonly string[] = [...]
const colors = Object.freeze(['red', 'green', 'blue'])
console.log(colors[0]) // 'red' — чтение OK
console.log(colors.length) // 3
// Мутирующие методы не работают:
try {
colors.push('yellow') // TypeError в strict mode
} catch (e) {
console.log(`push заблокирован: ${e.message}`)
}
// Немутирующие операции работают:
const withYellow = [...colors, 'yellow'] // создаём НОВЫЙ массив
console.log('Оригинал:', colors) // ['red', 'green', 'blue']
console.log('Новый:', withYellow) // ['red', 'green', 'blue', 'yellow']
// === Глубокая заморозка (как deep readonly в TS) ===
console.log('\n=== Поверхностная vs глубокая заморозка ===')
function deepFreeze(obj) {
Object.getOwnPropertyNames(obj).forEach(name => {
const value = obj[name]
if (typeof value === 'object' && value !== null) {
deepFreeze(value)
}
})
return Object.freeze(obj)
}
const config = deepFreeze({
server: { host: 'localhost', port: 3000 },
debug: true
})
config.server.port = 9999 // В TS as const: ошибка компиляции. В JS: игнорируется
console.log(config.server.port) // 3000 — не изменилосьreadonly запрещает переприсвоение свойства после инициализации. Это TypeScript-конструкция — в скомпилированном JS её нет.
interface Point {
readonly x: number
readonly y: number
}
const point: Point = { x: 10, y: 20 }
point.x = 5 // Ошибка TS: Cannot assign to 'x' because it is a read-only property
point.y = 8 // Ошибка TS: Cannot assign to 'y' because it is a read-only property
// Но создание нового объекта — OK:
const newPoint: Point = { x: point.x + 1, y: point.y }class Circle {
readonly radius: number
constructor(radius: number) {
this.radius = radius // OK — инициализация в конструкторе разрешена
}
grow() {
this.radius = this.radius + 1 // Ошибка: readonly
}
}const nums: ReadonlyArray<number> = [1, 2, 3]
// Или: readonly number[]
nums.push(4) // Ошибка: push не существует на ReadonlyArray
nums[0] = 10 // Ошибка: Index signature in type 'readonly number[]' only permits reading
nums.sort() // Ошибка: sort мутирует массив
// Разрешённые операции:
console.log(nums[0]) // OK — чтение
console.log(nums.length) // OK
const copy = [...nums] // OK — создание нового массиваas const делает значение **полностью неизменяемым** и выводит **литеральные типы** вместо широких типов.
// Без as const — широкие типы:
const config = {
host: 'localhost', // тип: string
port: 3000, // тип: number
debug: true // тип: boolean
}
// С as const — литеральные типы:
const config = {
host: 'localhost', // тип: 'localhost'
port: 3000, // тип: 3000
debug: true // тип: true
} as const
config.host = 'example.com' // Ошибка: readonly// Без as const:
const colors = ['red', 'green', 'blue']
// тип: string[] — потерян порядок и конкретные значения
// С as const:
const colors = ['red', 'green', 'blue'] as const
// тип: readonly ['red', 'green', 'blue'] — кортеж с литеральными типами
type Color = typeof colors[number]
// type Color = 'red' | 'green' | 'blue' — автоматически из массива!// Паттерн: as const для enum-like объектов
const STATUS = {
PENDING: 'pending',
ACTIVE: 'active',
CLOSED: 'closed',
} as const
type Status = typeof STATUS[keyof typeof STATUS]
// type Status = 'pending' | 'active' | 'closed'
function setStatus(status: Status) { /* ... */ }
setStatus(STATUS.ACTIVE) // OK
setStatus('pending') // OK
setStatus('unknown') // Ошибка TS!| | const | readonly |
|---|---|---|
| Применяется к | Переменным | Свойствам объектов |
| Что защищает | Переприсвоение переменной | Переприсвоение свойства |
| Уровень | Runtime (JS) | Compile-time (TS) |
| Глубина | Поверхностная | Поверхностная |
const не делает объект неизменяемым — только запрещает переприсвоение самой переменной. as const делает объект полностью readonly рекурсивно.
Демонстрация readonly-паттернов через Object.freeze и защиту от мутаций
// В JS readonly реализуется через Object.freeze()
// В TS — через модификатор readonly и as const (только compile-time)
// === Паттерн readonly объект (как TS readonly properties) ===
const point = Object.freeze({ x: 10, y: 20 })
console.log('=== Readonly объект ===')
console.log(point.x, point.y) // 10 20
// Попытка изменить — молча проигнорируется в non-strict режиме
point.x = 999 // В TS: Ошибка компиляции. В JS: просто игнорируется
console.log(point.x) // 10 — значение не изменилось
// === Паттерн as const для enum-like констант ===
const STATUS = Object.freeze({
PENDING: 'pending',
ACTIVE: 'active',
CLOSED: 'closed',
})
// В TS с as const: тип STATUS.PENDING === 'pending' (литерал, не string)
console.log('\n=== Enum-like константы ===')
console.log(STATUS.PENDING) // 'pending'
console.log(STATUS.ACTIVE) // 'active'
// Имитация type Status = typeof STATUS[keyof typeof STATUS]
const validStatuses = Object.values(STATUS)
function setStatus(status) {
if (!validStatuses.includes(status)) {
throw new Error(`Недопустимый статус: ${status}. Допустимые: ${validStatuses.join(', ')}`)
}
console.log(`Статус установлен: ${status}`)
}
setStatus(STATUS.ACTIVE) // 'Статус установлен: active'
setStatus('pending') // OK
try {
setStatus('unknown') // В TS: ошибка компиляции. В JS: runtime ошибка
} catch (e) {
console.log(e.message)
}
// === ReadonlyArray паттерн ===
console.log('\n=== Readonly массив ===')
// В TS: const colors: readonly string[] = [...]
const colors = Object.freeze(['red', 'green', 'blue'])
console.log(colors[0]) // 'red' — чтение OK
console.log(colors.length) // 3
// Мутирующие методы не работают:
try {
colors.push('yellow') // TypeError в strict mode
} catch (e) {
console.log(`push заблокирован: ${e.message}`)
}
// Немутирующие операции работают:
const withYellow = [...colors, 'yellow'] // создаём НОВЫЙ массив
console.log('Оригинал:', colors) // ['red', 'green', 'blue']
console.log('Новый:', withYellow) // ['red', 'green', 'blue', 'yellow']
// === Глубокая заморозка (как deep readonly в TS) ===
console.log('\n=== Поверхностная vs глубокая заморозка ===')
function deepFreeze(obj) {
Object.getOwnPropertyNames(obj).forEach(name => {
const value = obj[name]
if (typeof value === 'object' && value !== null) {
deepFreeze(value)
}
})
return Object.freeze(obj)
}
const config = deepFreeze({
server: { host: 'localhost', port: 3000 },
debug: true
})
config.server.port = 9999 // В TS as const: ошибка компиляции. В JS: игнорируется
console.log(config.server.port) // 3000 — не изменилосьСоздай функцию `createConfig(options)`, которая принимает объект с настройками и возвращает замороженную копию с дефолтными значениями через Object.freeze(). Дефолты: host = "localhost", port = 8080, debug = false. Переданные options должны переопределять дефолты.
Используй spread-оператор для слияния: const merged = { ...defaults, ...options }. Затем верни Object.freeze(merged). Порядок важен: дефолты идут первыми, options — вторыми, чтобы они перезаписывали дефолты.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке