as каже компілятору: «повір мені, тут такий тип». Під час виконання нічого не відбувається - ні перевірки, ні перетворення. Якщо розробник помилився, TypeScript мовчить, а помилка вилізе пізніше.
const user = (await response.json()) as User; // жодної перевірки
const input = document.querySelector('#email') as HTMLInputElement; // а якщо це <div> чи null?
Перевірка (звуження чи схема) доводить тип під час виконання:
const el = document.querySelector('#email');
if (!(el instanceof HTMLInputElement)) throw new Error('Поле email не знайдено');
el.value; // тепер справді HTMLInputElement
const user = UserSchema.parse(await response.json());
as не дозволяє будь-яке перетворення: 'text' as number - помилка, бо типи не перетинаються. Обхід через as unknown as number компілюється - і це майже завжди ознака проблеми в коді.
Споріднені конструкції з тими самими ризиками:
!(non-null assertion):user!.name- «тут точно неnull»;any- вимикає перевірку зовсім.
Коли as виправданий:
- TypeScript знає менше за вас, і це можна обґрунтувати: після власної перевірки, яку компілятор не розуміє, або в коді, що працює з DOM, де ви контролюєте розмітку;
as const- зовсім інша річ: не обхід перевірки, а звуження до літеральних типів (['draft', 'published'] as constдає кортеж рядкових літералів). Це безпечно й корисно;- тести - часткові фіктивні об'єкти (
{ id: 1 } as User); - межі зі сторонніми бібліотеками з неточними типами - з коментарем, чому.
Кращі альтернативи as:
satisfies- перевірити, що значення відповідає типу, не втрачаючи точного виведеного типу:
const routes = {
home: '/',
jobs: '/jobs',
} satisfies Record<string, string>;
// routes.home - літерал '/', а друкарська помилка в ключах типу Record<'home'|'jobs', string> була б помічена
- функції-перевірки типів (
value is User) і схеми; - анотація змінної (
const x: User = {...}) - вона перевіряє зайві й відсутні поля, аas- ні.
Правило лінтера @typescript-eslint/consistent-type-assertions і заборона as unknown as допомагають тримати кількість тверджень під контролем.