Type assertion говорит TypeScript: «доверяй мне, это точно этот тип». Компилятор соглашается без проверки:
const input = document.getElementById('username') as HTMLInputElement
console.log(input.value) // OK — TS знает что это HTMLInputElement
// Без assertion:
const raw = document.getElementById('username')
// raw: HTMLElement | null
console.log(raw.value) // Ошибка: Property 'value' does not exist on type 'HTMLElement'Assertions работают только если типы **частично совместимы** — нельзя привести string к number:
const x = 'hello' as number // Ошибка: Conversion of type 'string' to type 'number' may be a mistakeВосклицательный знак говорит TS: «это точно не null и не undefined»:
function getUser(): User | null { ... }
const user = getUser()
console.log(user!.name) // ! — я гарантирую что user не null
// Если user всё же null — TypeError в runtime!
// Лучше использовать явную проверку:
if (user !== null) {
console.log(user.name) // TypeScript сам сузит тип
}Когда типы полностью несовместимы, используют двойное приведение через unknown:
const x = 'hello' as unknown as number // Компилируется, но опасно!
// Реальный случай: принудительное приведение в тестах
const mockUser = {} as unknown as User
// Используется в моках, но в продакшне — антипаттернПоявился в TypeScript 4.9. Проверяет что значение **соответствует** типу, но сохраняет **точный выведенный тип**:
const palette = {
red: [255, 0, 0],
green: '#00ff00',
} satisfies Record<string, string | number[]>
// palette.red — TypeScript знает что это number[], а не string | number[]
// palette.green — TypeScript знает что это string, а не string | number[]
palette.red.map(x => x * 2) // OK — TS видит number[]
palette.green.toUpperCase() // OK — TS видит string
// С обычной аннотацией :Record<...> информация о точных типах теряется:
const palette2: Record<string, string | number[]> = { red: [255, 0, 0] }
palette2.red.map(x => x * 2) // Ошибка — TS думает это string | number[]// ОПАСНО: assertion без реальной проверки
async function loadUser(): Promise<User> {
const data = await fetch('/api/user').then(r => r.json())
return data as User // Нет гарантий что data действительно User!
}
// БЕЗОПАСНО: type guard с реальной проверкой
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
typeof (value as any).name === 'string' &&
typeof (value as any).age === 'number'
)
}
async function loadUserSafe(): Promise<User | null> {
const data = await fetch('/api/user').then(r => r.json())
return isUser(data) ? data : null
}**Правило:** assertions — последний выход. Предпочитай type guards, они безопаснее.
Runtime assertions: assertType, assertNotNull и TypedStorage — безопасное vs небезопасное приведение типов
// TypeScript assertions работают только при компиляции.
// В JS реализуем runtime-версии тех же идей.
class AssertionError extends Error {
constructor(message) {
super(message)
this.name = 'AssertionError'
}
}
// assertType — runtime аналог 'value as Type' (но безопасный: с проверкой)
function assertType(value, type, message) {
const actual = value === null ? 'null' :
Array.isArray(value) ? 'array' :
typeof value
if (actual !== type) {
throw new AssertionError(
message || `Expected type "${type}", got "${actual}"`
)
}
return value
}
// assertNotNull — runtime аналог non-null assertion (value!)
function assertNotNull(value, message) {
if (value === null || value === undefined) {
throw new AssertionError(message || `Expected non-null value, got ${value}`)
}
return value
}
// assertShape — runtime аналог type guard для объектов
function assertShape(value, shape) {
if (typeof value !== 'object' || value === null) {
throw new AssertionError(`Expected object, got ${typeof value}`)
}
for (const key of Object.keys(shape)) {
const expected = shape[key]
const actual = value[key] === null ? 'null' :
Array.isArray(value[key]) ? 'array' :
typeof value[key]
if (actual !== expected) {
throw new AssertionError(
`Property "${key}": expected "${expected}", got "${actual}"`
)
}
}
return value
}
// --- Демонстрация ---
console.log('=== assertType ===')
console.log(assertType('hello', 'string')) // 'hello'
console.log(assertType(42, 'number')) // 42
console.log(assertType([1,2,3], 'array')) // [1, 2, 3]
try {
assertType('hello', 'number')
} catch (e) {
console.log(e.name + ':', e.message) // AssertionError: Expected type "number", got "string"
}
console.log('\n=== assertNotNull ===')
const user = { name: 'Алексей' }
console.log(assertNotNull(user).name) // 'Алексей'
try {
assertNotNull(null, 'User не должен быть null')
} catch (e) {
console.log(e.message) // 'User не должен быть null'
}
console.log('\n=== assertShape ===')
const apiResponse = { name: 'Bob', age: 25, email: 'bob@test.com' }
const validated = assertShape(apiResponse, { name: 'string', age: 'number' })
console.log('Валидный объект:', validated.name) // 'Bob'
try {
assertShape({ name: 42, age: 'old' }, { name: 'string', age: 'number' })
} catch (e) {
console.log(e.message) // 'Property "name": expected "string", got "number"'
}Type assertion говорит TypeScript: «доверяй мне, это точно этот тип». Компилятор соглашается без проверки:
const input = document.getElementById('username') as HTMLInputElement
console.log(input.value) // OK — TS знает что это HTMLInputElement
// Без assertion:
const raw = document.getElementById('username')
// raw: HTMLElement | null
console.log(raw.value) // Ошибка: Property 'value' does not exist on type 'HTMLElement'Assertions работают только если типы **частично совместимы** — нельзя привести string к number:
const x = 'hello' as number // Ошибка: Conversion of type 'string' to type 'number' may be a mistakeВосклицательный знак говорит TS: «это точно не null и не undefined»:
function getUser(): User | null { ... }
const user = getUser()
console.log(user!.name) // ! — я гарантирую что user не null
// Если user всё же null — TypeError в runtime!
// Лучше использовать явную проверку:
if (user !== null) {
console.log(user.name) // TypeScript сам сузит тип
}Когда типы полностью несовместимы, используют двойное приведение через unknown:
const x = 'hello' as unknown as number // Компилируется, но опасно!
// Реальный случай: принудительное приведение в тестах
const mockUser = {} as unknown as User
// Используется в моках, но в продакшне — антипаттернПоявился в TypeScript 4.9. Проверяет что значение **соответствует** типу, но сохраняет **точный выведенный тип**:
const palette = {
red: [255, 0, 0],
green: '#00ff00',
} satisfies Record<string, string | number[]>
// palette.red — TypeScript знает что это number[], а не string | number[]
// palette.green — TypeScript знает что это string, а не string | number[]
palette.red.map(x => x * 2) // OK — TS видит number[]
palette.green.toUpperCase() // OK — TS видит string
// С обычной аннотацией :Record<...> информация о точных типах теряется:
const palette2: Record<string, string | number[]> = { red: [255, 0, 0] }
palette2.red.map(x => x * 2) // Ошибка — TS думает это string | number[]// ОПАСНО: assertion без реальной проверки
async function loadUser(): Promise<User> {
const data = await fetch('/api/user').then(r => r.json())
return data as User // Нет гарантий что data действительно User!
}
// БЕЗОПАСНО: type guard с реальной проверкой
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
typeof (value as any).name === 'string' &&
typeof (value as any).age === 'number'
)
}
async function loadUserSafe(): Promise<User | null> {
const data = await fetch('/api/user').then(r => r.json())
return isUser(data) ? data : null
}**Правило:** assertions — последний выход. Предпочитай type guards, они безопаснее.
Runtime assertions: assertType, assertNotNull и TypedStorage — безопасное vs небезопасное приведение типов
// TypeScript assertions работают только при компиляции.
// В JS реализуем runtime-версии тех же идей.
class AssertionError extends Error {
constructor(message) {
super(message)
this.name = 'AssertionError'
}
}
// assertType — runtime аналог 'value as Type' (но безопасный: с проверкой)
function assertType(value, type, message) {
const actual = value === null ? 'null' :
Array.isArray(value) ? 'array' :
typeof value
if (actual !== type) {
throw new AssertionError(
message || `Expected type "${type}", got "${actual}"`
)
}
return value
}
// assertNotNull — runtime аналог non-null assertion (value!)
function assertNotNull(value, message) {
if (value === null || value === undefined) {
throw new AssertionError(message || `Expected non-null value, got ${value}`)
}
return value
}
// assertShape — runtime аналог type guard для объектов
function assertShape(value, shape) {
if (typeof value !== 'object' || value === null) {
throw new AssertionError(`Expected object, got ${typeof value}`)
}
for (const key of Object.keys(shape)) {
const expected = shape[key]
const actual = value[key] === null ? 'null' :
Array.isArray(value[key]) ? 'array' :
typeof value[key]
if (actual !== expected) {
throw new AssertionError(
`Property "${key}": expected "${expected}", got "${actual}"`
)
}
}
return value
}
// --- Демонстрация ---
console.log('=== assertType ===')
console.log(assertType('hello', 'string')) // 'hello'
console.log(assertType(42, 'number')) // 42
console.log(assertType([1,2,3], 'array')) // [1, 2, 3]
try {
assertType('hello', 'number')
} catch (e) {
console.log(e.name + ':', e.message) // AssertionError: Expected type "number", got "string"
}
console.log('\n=== assertNotNull ===')
const user = { name: 'Алексей' }
console.log(assertNotNull(user).name) // 'Алексей'
try {
assertNotNull(null, 'User не должен быть null')
} catch (e) {
console.log(e.message) // 'User не должен быть null'
}
console.log('\n=== assertShape ===')
const apiResponse = { name: 'Bob', age: 25, email: 'bob@test.com' }
const validated = assertShape(apiResponse, { name: 'string', age: 'number' })
console.log('Валидный объект:', validated.name) // 'Bob'
try {
assertShape({ name: 42, age: 'old' }, { name: 'string', age: 'number' })
} catch (e) {
console.log(e.message) // 'Property "name": expected "string", got "number"'
}Реализуй класс `TypedStorage` — обёртка над хранилищем с методами `setItem(key, value)` (сериализует через JSON.stringify) и `getItem(key)` (десериализует через JSON.parse и возвращает значение или null). Используй обычный JS объект в качестве хранилища (без браузерного localStorage).
Инициализируй this._storage = {}. В setItem: this._storage[key] = JSON.stringify(value). В getItem: проверь наличие ключа через !(key in this._storage), затем JSON.parse(this._storage[key]). В removeItem: delete this._storage[key].
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке