Duck-Typing у TypeScript та правило “Always Defaults”
TypeScript, як надмножина JavaScript, додає статичну типізацію до динамічного світу розробки на JavaScript. Однією з його основних функцій є duck-typing (типізація «як качка»), що дозволяє створювати лаконічний і водночас безпечний код. У поєднанні з правилом Always Defaults («Завжди використовуйте значення за замовчуванням») розробники можуть мінімізувати помилки типізації та типові проблеми на етапі виконання, роблячи код більш стабільним і надійним.
У цій статті ми розглянемо duck-typing у TypeScript, переваги використання значень за замовчуванням замість явного оголошення типів для змінних і те, як це правило допомагає уникати таких помилок виконання, як «object has no property or method».
Що таке Duck-typing у TypeScript?
Duck-typing базується на принципі: «Якщо це виглядає, як качка, плаває, як качка, і крякає, як качка, то, ймовірно, це качка.» У TypeScript цей принцип дозволяє перевіряти типи об’єктів на основі їхньої структури, а не оголошених типів.
Наприклад:
let isTyped = true; // typeof isTyped is boolean by default let index = 0; // typeоf index is number by default let name = ''; // typeof name is string by default const duck = { quack: () => console.log("Кря!"), } interface Quackable { quack(): void; } const makeItQuack = (animal: Quackable) => { animal.quack(); } makeItQuack(duck); // Працює, тому що `duck` має метод `quack`
У цьому прикладі об’єкт duck відповідає структурі інтерфейсу Quackable, тому TypeScript дозволяє його використання без явного оголошення типу.
Правило «Always Defaults»: чому значення за замовчуванням краще за типи
Оголошуючи змінні та параметри функцій додавання значень за замовчуванням може бути більш практичним захищенням від помилок рантайму, ніж тільки визначення типу. Це правило гарантує, що змінні завжди матимуть як тип, так і початкове значення, зменшуючи ризик виникнення помилок на етапі виконання, пов’язаних з undefined або null.
Приклад без значень за замовчуванням
Розглянемо наступну ситуацію:
function greet(name: string) { console.log(`Привіт, ${name}!`); } greet(); // Помилка: Очікується 1 аргумент, але отримано 0.
Якщо при виклику функції не вказати параметр name, TypeScript викличе помилку компіляції. Це може призвести до проблем у коді, якщо не бути уважним.
Приклад зі значеннями за замовчуванням
Тепер застосуємо правило Always Defaults:
function greet(name = "Гість") { console.log(`Привіт, ${name}!`); } greet(); // Виведе: Привіт, Гість!
У цьому випадку, якщо значення name не передано, воно автоматично встановлюється як «Гість». Такий підхід усуває помилки на етапі виконання та забезпечує передбачувану поведінку.
Переваги правила «Always Defaults»
1. Покращення читабельності функцій
- Розробникам буде легше зрозуміти призначення функції, якщо вони бачитимуть значення аргументів
- Розмір коду зменшиться завдяки видаленню зайвих typescript анотацій
- Завдяки параметрам за замовчуванням функції залишаються зручними навіть у разі пропуску деяких аргументів
2. Усунення помилок undefined та null, «object has no property or method»
Значення за замовчуванням гарантують, що змінні завжди ініціалізовані валідним значенням, усуваючи у більшості випадків потребу в зайвих перевірках.
Duck-typing у поєднанні зі обов’язковими значеннями за замовчуванням забезпечує, що передані об’єкти містять усі необхідні властивості чи методи, запобігаючи помилкам під час виконання.
3. Сумісність з JavaScript
Якщо ви використовуєте правило Always Defaults на JavaScript проектах, це вже зробить ваш код максимально сумісним з TypeScript, що значно полегшить подальший перехід на повну статичну типізацію.
Недоліки правила «Always Defaults»
Використання правила «Always Defaults» має багато переваг, але є також кілька потенційних ситуацій, у яких воно може бути неефективним.
1. Маскування логічних помилок
Якщо значення за замовчуванням задається без належного аналізу, це може приховувати помилки у коді.
- Коли важливі обов’язкові значення. Якщо деякі змінні чи аргументи повинні бути явно переданими (наприклад, userId), дефолтні значення створюють ілюзію «коректної» роботи програми.
- Коли є складна бізнес-логіка. У випадках, коли значення має бути отримане з конкретного джерела, дефолтне значення може приховати помилки в цьому джерелі (наприклад, помилка в API).
- Відсутність валідаторів. Без перевірки вхідних даних програма може виконуватися неправильно, не показуючи, що щось пішло не так.
2. Циклічна ініціалізація
Задаючи значення за замовчуванням циклічно, можна створити великі масиви, які насправді не потрібні, що призведе до марного витрачання пам’яті або ресурсів.
3. Не оптимально для залежностей
Якщо значення за замовчуванням залежить від зовнішніх ресурсів (наприклад, результату виклику API або іншої функції), використання «Always Defaults» може ускладнити або сповільнити виконання коду:
const getDefaultValue = () => fetch("https://api.example.com/config"); let config = getDefaultValue(); // Виникає затримка через асинхронність.
Як уникнути: Використовувати lazy initialization (відкладену ініціалізацію) для складних або асинхронних значень.
Практичне застосування
Значення за замовчуванням для змінних
Замість цього:
let count: number; count++; // Помилка: Змінна 'count' використовується до присвоєння значення.
Краще так:
let count = 0; count++; // Працює без проблем
Значення за замовчуванням для параметрів функцій
Замість цього:
function fetchData(url: string, retries?: number) { const attempts = retries || 3; console.log(`Завантажую ${url} з ${attempts} спробами`); }
Краще так:
function fetchData(url = "", retries = 3) { console.log(`Завантажую ${url} з ${retries} спробами`); }
Значення за замовчуванням у деструктуризації об’єктів
Коли працюєте з об’єктами, задавайте значення за замовчуванням:
function configure({ retries, timeout } = { retries: 3, timeout: 1000 }) { console.log(`Кількість спроб: ${retries}, Таймаут: ${timeout}`); } configure(); // Виведе: Кількість спроб: 3, Таймаут: 1000
Висновки
• Duck-typing дозволяє TypeScript перевіряти типи на основі структури даних, а не оголошених типів.
• Правило «Always Defaults»
Завжди використовувати значення за замовчуванням — не найкраща ідея. Можливо, це допоможе уникнути падіння застосунку під час виконання, але водночас створює ідеальні умови для появи багів і логічних помилок. У результаті додаток працює без видимих збоїв, проте не виконує очікувані дії.
Обов’язкові аргументи функцій критично важливі для коректної роботи. Уявімо функцію, яка надсилає електронні листи. Для неї обов’язковими параметрами є адреса отримувача і вміст листа. Як розробника, тестувальника або кінцевого користувача мене абсолютно не влаштує ситуація, коли помилка не виникає, але лист не доходить до потрібного отримувача. Наявність явної помилки дозволила б одразу зрозуміти, що щось пішло не так.
55 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів