Skip to main content
Практика завдань

Типові асерції в TypeScript

Що таке типові твердження?

Типові твердження — це спосіб повідомити компілятору TypeScript: "Я знаю тип цього значення краще, ніж ти". Це не приведення типу і не змінює значення під час виконання — це лише підказка для компілятора.


Синтаксис

TypeScript підтримує два синтаксиси для типових тверджень:

Синтаксис as

typescript
const value: any = "hello"; const length = (value as string).length; // 5

Синтаксис з кутовими дужками

typescript
const value: any = "hello"; const length = (<string>value).length; // 5

Важливо:

У JSX/TSX можна використовувати лише синтаксис as, оскільки кутові дужки конфліктують із синтаксисом JSX.


Коли використовувати типові твердження?

Робота з DOM

typescript
// TypeScript не знає, який елемент буде повернуто const input = document.getElementById('email'); // HTMLElement | null // Твердження конкретного типу const emailInput = document.getElementById('email') as HTMLInputElement; emailInput.value = 'test@example.com'; // Альтернатива const emailInput2 = <HTMLInputElement>document.getElementById('email');

Робота з API та будь-яким типом

typescript
const response: any = await fetch('/api/user').then(r => r.json()); interface User { id: number; name: string; email: string; } const user = response as User; console.log(user.name);

Звуження об'єднаних типів

typescript
type Result = { success: true; data: string } | { success: false; error: string }; function handleResult(result: Result) { if (result.success) { // TypeScript знає, що це { success: true; data: string } console.log(result.data); } else { // TypeScript знає, що це { success: false; error: string } console.log(result.error); } } // Але іноді потрібне явне твердження: const result = getResult(); const successResult = result as { success: true; data: string };

Робота з літералами

typescript
// TypeScript виводить тип як string const method = 'GET'; // string // Потрібен конкретний літерал const method2 = 'GET' as const; // 'GET' // Або const method3 = 'GET' as 'GET' | 'POST';

Типові твердження vs Приведення типу

Типові твердження (TypeScript)

typescript
const value: any = "42"; const num = value as number; // Компільовано, але помилка під час виконання! console.log(num * 2); // NaN (рядок помножений на число)

Приведення типу (інші мови)

javascript
// В інших мовах приведення конвертує значення String num = "42"; int converted = (int) num; // Насправді конвертує рядок у число

Важливо:

Типові твердження не виконують конверсію і не перевіряють тип під час виконання. Це лише підказка для компілятора.


Подвійні твердження

Іноді TypeScript не дозволяє пряме твердження між несумісними типами:

typescript
const value: string = "hello"; // Помилка: Конверсія типу 'string' у тип 'number' може бути помилкою // const num = value as number; // Подвійне твердження через unknown (або any) const num = value as unknown as number;

Увага:

Подвійні твердження є ознакою проблем з типами. Використовуйте лише як останній засіб.


Оператор твердження про ненульовість

Оператор ! повідомляє TypeScript, що значення точно не є null або undefined.

Синтаксис

typescript
// TypeScript вважає, що це може бути null const element = document.getElementById('root'); // HTMLElement | null // Ми впевнені, що елемент існує const elementNonNull = document.getElementById('root')!; // HTMLElement elementNonNull.innerHTML = 'Hello';

Приклади використання

typescript
interface User { name: string; email?: string; } const user: User = { name: 'John', email: 'john@example.com' }; // Помилка: Об'єкт може бути 'undefined' // const emailLength = user.email.length; // З перевіркою if (user.email) { const emailLength = user.email.length; } // З твердженням про ненульовість (якщо впевнені) const emailLength = user.email!.length;

З необов'язковим ланцюгом

typescript
interface Config { api?: { url?: string; }; } const config: Config = { api: { url: 'https://api.example.com' } }; // Без твердження про ненульовість const url = config.api?.url; // string | undefined // З твердженням про ненульовість const urlNonNull = config.api!.url!; // string

Увага:

Твердження про ненульовість обходить перевірки TypeScript. Якщо значення виявиться null або undefined, ви отримаєте помилку під час виконання.


Твердження const

as const створює типи літералів, що не можуть бути змінені.

Примітиви

typescript
// Звичайне оголошення let x = 'hello'; // string // З as const let y = 'hello' as const; // 'hello' (літеральний тип)

Об'єкти

typescript
// Звичайний об'єкт const config = { host: 'localhost', port: 3000 }; // { host: string; port: number; } // З as const const configConst = { host: 'localhost', port: 3000 } as const; // { readonly host: 'localhost'; readonly port: 3000; } // Не можна змінювати // configConst.port = 8080; // Помилка

Масиви

typescript
// Звичайний масив const colors = ['red', 'green', 'blue']; // string[] // З as const const colorsConst = ['red', 'green', 'blue'] as const; // readonly ['red', 'green', 'blue'] type Color = typeof colorsConst[number]; // 'red' | 'green' | 'blue'

Практичне застосування

typescript
// Створення об'єднаного типу з масиву const ROLES = ['admin', 'user', 'guest'] as const; type Role = typeof ROLES[number]; // 'admin' | 'user' | 'guest' function checkRole(role: Role) { // ... } checkRole('admin'); // OK // checkRole('moderator'); // Помилка

Оператор Satisfies (TypeScript 4.9+)

satisfies перевіряє відповідність типу, зберігаючи виведення типу.

typescript
type Colors = 'red' | 'green' | 'blue'; const colors = { red: [255, 0, 0], green: '#00ff00', blue: [0, 0, 255] } satisfies Record<Colors, string | number[]>; // TypeScript зберіг точні типи colors.red; // number[] colors.green; // string colors.blue; // number[] // Якщо б ми використали типове твердження: const colors2: Record<Colors, string | number[]> = { red: [255, 0, 0], green: '#00ff00', blue: [0, 0, 255] }; colors2.red; // string | number[] (втрата точності)

Коли НЕ використовувати типові твердження

Замість правильного типізування

typescript
// Погано function getUser() { return { id: 1, name: 'John' } as any; } // Добре interface User { id: number; name: string; } function getUser(): User { return { id: 1, name: 'John' }; }

Для виправлення помилок типізації

typescript
// Погано - приховування проблеми const value: number = "hello" as any as number; // Добре - виправлення проблеми const value: string = "hello"; const num: number = parseInt(value, 10);

Коли можна використовувати захисні механізми типів

typescript
function processValue(value: string | number) { // Погано const str = value as string; return str.toUpperCase(); // Добре if (typeof value === 'string') { return value.toUpperCase(); } return value.toString(); }

Безпечні альтернативи

Захисні механізми типів

typescript
function isString(value: any): value is string { return typeof value === 'string'; } const value: any = "hello"; if (isString(value)) { // TypeScript знає, що value є рядком console.log(value.toUpperCase()); }

Розрізнені об'єднання

typescript
type Shape = | { kind: 'circle'; radius: number } | { kind: 'square'; size: number }; function getArea(shape: Shape) { switch (shape.kind) { case 'circle': return Math.PI * shape.radius ** 2; case 'square': return shape.size ** 2; } }

Необов'язковий ланцюг

typescript
// Замість твердження про ненульовість const value = obj.prop!.nested!.value; // Використовуйте необов'язковий ланцюг const value = obj.prop?.nested?.value; // string | undefined

Загальні помилки

Твердження несумісних типів

typescript
const num = 42; // Помилка: не можна конвертувати число в рядок // const str = num as string; // Потрібне подвійне твердження (але це погано!) const str = num as unknown as string;

Ігнорування помилок через as any

typescript
// Погано function processData(data: ComplexType) { return (data as any).someMethod(); } // Добре function processData(data: ComplexType) { if ('someMethod' in data && typeof data.someMethod === 'function') { return data.someMethod(); } }

Втрата безпеки типів

typescript
// Погано - втрата безпеки типів const users = getUsers() as any[]; // Добре interface User { id: number; name: string; } const users = getUsers() as User[];

Практичні патерни

Безпечне витягування з DOM

typescript
function getElement<T extends HTMLElement>(id: string): T | null { return document.getElementById(id) as T | null; } const input = getElement<HTMLInputElement>('email'); if (input) { input.value = 'test'; }

Робота з unknown

typescript
function parseJSON(json: string): unknown { return JSON.parse(json); } const data = parseJSON('{"name": "John"}'); // Захисний механізм для безпеки function isUser(data: unknown): data is { name: string } { return typeof data === 'object' && data !== null && 'name' in data && typeof (data as any).name === 'string'; } if (isUser(data)) { console.log(data.name); }

Висновок

Типові твердження:

  • Не конвертують значення, лише вказують тип компілятору
  • Синтаксис: as (бажано) або <> (не в JSX)
  • Корисні при роботі з DOM та any
  • Твердження про ненульовість ! усуває null | undefined
  • as const створює літерали, що не можуть бути змінені
  • satisfies (4.9+) перевіряє тип, зберігаючи виведення
  • Використовуйте обережно — можуть приховувати проблеми

На співбесідах:

Важливо вміти:

  • Пояснити різницю між типовими твердженнями та приведенням типу
  • Показати обидва синтаксиси (as та <>)
  • Пояснити, коли використовувати as const
  • Обговорити оператор твердження про ненульовість
  • Надати приклади безпечних альтернатив (захисні механізми типів)
  • Пояснити ризики надмірного використання тверджень

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?
Практика завдань