value as T - твердження типу: розробник каже компілятору «вважай це значення типом T». Нічого не перетворюється й не перевіряється під час виконання.
const user = (await response.json()) as User;
user.email.toLowerCase(); // впаде, якщо API повернув { error: '...' }
Компілятор довіряє, а реальні дані можуть бути іншими. Помилка переноситься з місця, де дані прийшли, у випадкове місце далі в коді.
Що as дозволяє, а що ні:
- звужувати й розширювати в межах сумісних типів (
unknown→User,HTMLElement→HTMLInputElement); - приведення між несумісними типами (
'текст' as number) - помилка компіляції. Але подвійнеas unknown as Tобходить і це - верна ознака, що щось не так.
Альтернативи:
1. Перевірка під час виконання для зовнішніх даних (API, localStorage, форми):
import { z } from 'zod';
const UserSchema = z.object({ id: z.number(), email: z.email() });
type User = z.infer<typeof UserSchema>;
const user = UserSchema.parse(await response.json()); // кидає помилку, якщо дані не ті
2. Користувацький type guard:
function isUser(value: unknown): value is User {
return typeof value === 'object' && value !== null && 'email' in value;
}
3. Звуження вбудованими перевірками: instanceof, typeof, in, перевірка поля-дискримінатора.
4. Типізоване API замість приведення: document.querySelector<HTMLInputElement>('input[name=email]') замість as HTMLInputElement; генерік у useState<User | null>(null).
5. satisfies для об'єктів-літералів - перевірка форми без втрати точного типу.
Коли as допустимий:
as const- не приведення, а звуження до літеральних типів;- тести й моки (
{} as Partial<Service> as Service) - з розумінням ризику; - місця, де ви знаєте більше за компілятор і це очевидно з контексту (наприклад, після власної перевірки, яку TypeScript не розпізнав).
Правило лінтера @typescript-eslint/consistent-type-assertions дає змогу заборонити as для об'єктних літералів і змусити використовувати анотації чи satisfies.