Оволодіння типом `never` у TypeScript: Використання та основні відмінності

pic

Фото від Nangialai Stoman на Unsplash

Чи колись ви дивилися на визначення типу TypeScript і відчували повне здивування? Я точно так відчував. Коли я вперше зустрів таке, як const createToken: (variables: never, options?: Options | undefined) => void, я був вражений. Як можна взагалі передати вхідні дані, коли очікуваний тип — це never? Здавалося, що це непосильне завдання! Це початкове здивування привело мене до глибшого розуміння типу never, і я впевнений, що багато розробників TypeScript можуть відчути те саме.

Тип never у TypeScript — це важливий інструмент для вираження неможливих станів і забезпечення типобезпеки. Він представляє тип, до якого не може належати жодне значення. Цей пост досліджує, що таке never, чому він важливий, як він порівнюється з undefined і null, і, найголовніше, його практичні застосування — зокрема, чому ви можете зустріти його як тип аргументу, як я це зробив!

Чому використовувати never? Розуміння неможливості

pic

Фото від Nick Fewings на Unsplash

Тип never забезпечує неможливість, на відміну від інших типів, що можуть означати порожнечу або відсутність. Давайте порівняємо його з кількома схожими концепціями:

  • undefined: Означає змінну, яка була оголошена, але ще не отримала значення.
  • null: Означає навмисно порожнє або відсутнє значення.
  • void: Означає відсутність значення, яке повертає функція (або функцію, яка не повертає нічого явно).

Тип never піднімає концепцію порожнечі на новий рівень. Він означає абсолютну неможливість, запобігаючи неправильним станам або досягненню недосяжного коду у вашій програмі. Це не просто про те, щоб щось було порожнім; це про те, щоб щось було неможливим.

Основні випадки використання типу never

pic

Фото від Alexandre St-Louis на Unsplash

1. Обмеження недійсних параметрів: Це саме те, що викликало моє початкове здивування при роботі з прикладом createToken(variables: never, ...). Коли параметр функції має тип never, це сигналізує, що цей параметр не повинен використовуватися під час звичайної роботи. Це означає, що параметр або не потрібен, або логіка налаштована так, що будь-яка спроба використати цей параметр призведе до помилки або до стану, з якого неможливо відновитися.

function handleInvalid(input: never): void {  
 throw new Error(`Invalid input: $`);   
} // У випадку з `createKey(variables: never, ...)` ймовірно використовується ось так:   
// Якщо логіка написана так, що жодна змінна не потрібна, вона може бути типу `never`.   
// Логіка функції не буде очікувати чи використовувати значення для цього вхідного параметра.

2. Забезпечення вичерпних перевірок: Тип never є незамінним при роботі з дискримінованими об'єднаннями або коли потрібно переконатися, що всі можливі варіанти обробляються в конструкціях switch або if/else.

type Status = "success" | "error";   
function handleStatus(status: Status): void {  
 if (status === "success") {  
 console.log("Success!");  
 } else if (status === "error") {  
 console.log("Error!");  
 } else {  
 const _exhaustiveCheck: never = status; // Помилка перевірки типу, якщо статус не оброблений  
 throw new Error(`Unexpected status: ${status}`);  
 }  
}
  • Примітка: Я перейменував змінну unreachable на _exhaustiveCheck, щоб краще відобразити її мету в контексті вичерпних перевірок. Також краще практикувати початок імені змінної з підкреслення _, щоб позначити, що змінна призначена лише для перевірки типів. Я додав оператор throw, оскільки в оригінальному прикладі не було помилки під час виконання.

3. Фільтрація об'єднань (за допомогою Exclude): Тип never грає важливу роль за лаштунками при маніпулюванні типами об'єднань.
Розглянемо утиліту типу Exclude:

type Keys = "name" | "age" | "password";   
type VisibleKeys = Exclude; // "name" | "age"

Exclude використовує умовні типи, і коли ви видаляєте конкретний елемент з об'єднання, цей елемент фактично стає типом never, перш ніж його відфільтрують.

undefined як never - Розповсюджений трюк

Ви можете зустріти код, де undefined явно перетворюється на never. Це часто робиться в контексті вичерпних перевірок або коли гілка коду повинна бути неможливою. Справа не в тому, що undefined фактично є never; це спосіб забезпечити неможливий стан у вашому коді.

function exhaustiveCheck(value: never): void {  
 console.error("Unexpected value:", value);  
 // Примітка: Цей код ніколи не виконається, якщо вичерпні перевірки зроблені коректно  
 // тому що параметр функції `value` має тип never  
}  

switch ("unexpectedCase") {  
 default:  
 // Ця лінія викликає помилку TS, оскільки неможливо передати значення типу never у функцію.  
 // Однак це гарний спосіб показати використання.  
 exhaustiveCheck(undefined as never);  
}

never vs. undefined vs. null: Порівняння

pic

Приклад:

let a: never;  
let b: undefined = undefined;  
let c: null = null;  
// a = undefined; // Помилка: Тип 'undefined' не може бути присвоєний типу 'never'.  
// a = null; // Помилка: Тип 'null' не може бути присвоєний типу 'never'.

Висновок

pic

Фото від Randy Rizo на Unsplash

Тип never — потужний інструмент для забезпечення вичерпних перевірок, відхилення недійсних станів та забезпечення суворої типобезпеки. Хоча він означає "нічого", його мета далеко не тривіальна. Він є основою потужної системи типів TypeScript. Спочатку це може здатися заплутаним, особливо коли він з'являється в несподіваних місцях, таких як параметри функцій, але розуміння того, як never сигналізує, що щось не повинно існувати, є ключем до повного використання TypeScript. Прийнявши тип never, ви зможете писати більш надійні, типобезпечні та підтримувані додатки на TypeScript.

Перекладено з: Mastering TypeScript’s never Type: Usage and Key Differences

Leave a Reply

Your email address will not be published. Required fields are marked *