Get Mystery Box with random crypto!

Дуже крута доповідь Тіма Зуханека(Tim Suchanek) Generics, Cond | Radio Kottans

Дуже крута доповідь Тіма Зуханека(Tim Suchanek) Generics, Conditional types and Mapped types надихнула нас поговорити про Advanced Types у TypeScript. Обов’язково послухайте Тіма, проте хочемо дещо додати від себе. В один допис усе не влізло, тому “далі буде..."

TypeScript Advanced Types ʕ •ᴥ•ʔ

Про що йдеться? Перший рівень роботи з TypeScript – досить інтуїтивний – це брати наявні типи та комбінувати їх для опису об’єктів доменної області. Беремо string, number, Array, отримуємо щось таке:
type User = {
name: string,
age: number,
hobbies: string[]
}
function registerUser(user: User): number { /* return registration number */ }

This is fine, але згодом типів, як і будь-якого іншого коду, стає забагато і ми згадаємо про DRY та SOLID. Було б класно використати типи повторно та зробити їх гнучкими. Іншими словами, ми хочемо працювати з типами як зі значеннями, бо звикли до цього у програмуванні.

У доповіді, яка стала приводом для цього допису, наведений такий приклад. Тім Зуханек працює над ORM Prisma. Його команда зіткнулася з проблемою безпечної типізації запитів до БД. Справа у тому, що користувач може скласти запит (query) таким чином, що потребуватиме лише певні поля з моделі. Для типізації результату такого запиту необхідно обраховувати типи динамічно в залежності від змісту запиту.

Отже, ми хочемо визначати власні типи на базі існуючих, і робити це динамічно. Що пропонує нам з цього приводу TypeScript? Запрошуємо у чарівний світ операцій над типами.

Операції над типами – складна тема, адже вони вимагають переходу на новий рівень абстракції (ми працюємо не зі значеннями, а з типами значень). Проте ми можемо порівняти їз з чимось, що нам знайоме краще, наприклад, з можливостями мови програмування. Інструменти системи типів сильно відрізняються від звичних нам інструментів мови програмування і аналогії можуть бути невичерпниими чи неточними у певних випадках, але вони допоможуть зламати кригу і тому, на нашу думку, ними доречно скористатися.

Generics

Звернімося до Вікіпедії:
Узагальнене програмування (generic programming) - стиль програмування, що описує алгоритми, не вказуючи конкретні типи значень, що приймають участь в обчисленнях. Ці типи будуть визначені пізніше, залежно від типів, наданих як параметри.

Що нам нагадують типи, що невідомі заздалегідь та будуть призначені при виконанні? Параметри функції.
Generics це параметризований типи. До дженериків відносяться зокрема масиви (ми можемо описати типи елементів масиву), проміси (яке саме значення містить проміс?) і багато інших вбудований типів, але і свої дженерики ми теж можемо писати. Дженерики у TS мають власний синтаксис – кутові дужки після типу, у яких ми вказуємо типи-параметри. Скажімо, у нас є тип, що описує об’єкт із властивістю value змінного типу. Тип T у визначенні дженерика вказує на параметр дженерика (той самий тип, не відомий заздалегідь)

type MyParametrizedType = { value: T}

На базі такого типу ми можем створити похідний тип, де обє’кт має властивість value вже конкретного типу:

type MyStringValueType = MyParametrizedType
const stringValueObject: MyStringValueType = {value: "hello"}

type MyNumberValueType = MyParametrizedType
const numberValueObject: MyNumberValueType = {value: 42}

При визначенні типів важливо подумати не лише про те як описати дозволені типи, а і про те, як заборонити недозволені. Тому перевіримо, чи наші типи насправді обмежують небажані значення. Перевіримо не тілько позитивні, але і негативні сценарії:

const brokenObject1: MyStringValueType = {value: 42} // error
const brokenObject2: MyNumberValueType = {value: "hello"} // error

Так, очікувані помилки з’явилися! Можемо рухатися далі.

Далі буде...