Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni

🤖🚫 Контент вільний від AI. Цей пост на 100% написаний людиною, як і все на моєму блозі. Насолоджуйтесь!

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. Тобто десь все одно буде перевірка. Різниця тільки в тому, що краще розмістити її якнайвище в алгоритмі, та в такому місці, де конкретизація до пунктів-задач буде очікуваною: ззовні у нас всілякі пункти, а всередині — тільки задачі.

Як бачите, різниця тут не в правильності (всі варіанти однаково правильні), а в наочності в поясненні людям. В цьому треба спиратися на систему типів, а не сперечатися з нею.