Постфіксний ! каже компілятору: «це значення точно не null і не undefined». Компілятор вірить на слово й прибирає null | undefined з типу.
const input = document.querySelector('#email')!; // HTMLElement замість HTMLElement | null
const price = prices.get('coffee')!; // number замість number | undefined
! нічого не перевіряє під час виконання. Якщо значення все ж null, помилка виникне пізніше й далеко від причини: Cannot read properties of null. TypeScript саме для того й попереджав.
Коли ! доречний:
- значення гарантовано існує з причин, яких компілятор не бачить, і ця гарантія очевидна поруч у коді;
- ініціалізація, яку TypeScript не відстежує (поле класу, яке заповнює фреймворк), - хоча для полів краще
field!: Typeу оголошенні з коментарем, ніж!при кожному використанні.
Краще альтернативи в більшості випадків:
- явна перевірка з помилкою - падіння одразу з зрозумілим текстом:
const input = document.querySelector<HTMLInputElement>('#email');
if (!input) throw new Error('Поле #email не знайдено');
input.value; // тут уже HTMLInputElement
- функція-помічник:
function assertDefined<T>(value: T, message: string): asserts value is NonNullable<T> {
if (value == null) throw new Error(message);
}
- опціональний ланцюжок і значення за замовчуванням, якщо відсутність нормальна:
prices.get('coffee') ?? 0; - переписати код так, щоб тип був точним: зберігати знайдений елемент у змінній після перевірки, а не шукати двічі.
Типові місця, де ! приховує баги:
map.get(key)!- ключа може не бути;array.find(...)!- елемента може не знайтися;useRef<HTMLDivElement>(null).current!в ефекті, що може виконатися до монтування;process.env.API_KEY!- змінну оточення можуть не задати.
Лінтер (@typescript-eslint/no-non-null-assertion) змушує обґрунтовувати кожен ! - корисне правило для команди.
Важливо: strict у TypeScript 6+ увімкнено за замовчуванням, тож перевірки на null діють навіть без явного "strict": true - і ! стає помітнішою «дірою» в цих перевірках.