Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni
🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!19.09.2023
Перевірка на наявність поля в TypeScript: від поганого до кращого
В продовження обмежень TypeScript. Припустимо маємо такий собі interface ListItem { taskDates?: { due: string } }
. Якщо цей пункт списку — задача, то він має дати. Якщо ні — то не має. Ми пишемо код, який щось робить з цими датами (наприклад, обирає задачі зі строком на сьогодні.)
Криво: Оператор невпевненості, або як його справді називають, “безпечної навігації”.
if (listItem.taskDates?.due === today) { ... }
Цей код нічого не каже про сенс наявності taskDates
. Він просто припускає, що їх може не бути. Він семантично порожній. Хоч такий код і працює, але його читач залишається здогадуватись про зміст самотужки. Потім проєктом розповсюджуються захисні конструкції, а з ними — невпевненість. Є значення? Немає? Перевірю на всяк випадок, хто його зна.
Чітко: Перевіряти на наявність поля на початку функції.
if (!listItem.taskDates) {
return; // або навіть throw
}
if (listItem.taskDates.due === today) { ... }
Тут навпаки ми явно кажемо: якщо дат немає — то ця функція втрачає свій сенс.
В залежності від контексту, можна або просто вертати, або кидати помилку (тоді зовнішній код зобовʼязується викликати функцію тільки коли дати є). Читач розуміє, що код розрахований саме на елементи, в яких є taskDates
.
Майстерно: обмежувати тип так, щоб виклик потребував чіткого набору полів.
interface TaskListItem {
taskDates: { due: string };
}
// або з utility-types:
type TaskListItem = Required<ListItem, "taskDates">;
Оскільки в TypeScript типи порівнюються за змістом, то коду, який викликає нашу функцію, не треба утворювати новий обʼєкт. Достатньо тільки перевірити, що поле присутнє, та обʼєкт типу ListItem
задовольнить тип TaskListItem
. Тобто десь все одно буде перевірка. Різниця тільки в тому, що краще розмістити її якнайвище в алгоритмі, та в такому місці, де конкретизація до пунктів-задач буде очікуваною: ззовні у нас всілякі пункти, а всередині — тільки задачі.
Як бачите, різниця тут не в правильності (всі варіанти однаково правильні), а в наочності в поясненні людям. В цьому треба спиратися на систему типів, а не сперечатися з нею.