Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Чому потрібен TypeScript, переваги та недоліки». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**TypeScript** - це надбудова над JavaScript зі статичними типами, яка виявляє помилки в редакторі до запуску коду. ```typescript let age: number = 25; age = "25"; // Помилка: Type 'string' is not assignable to type 'number' ``` **Головне:** після компіляції типи зникають; браузер і Node.js запускають звичайний JavaScript.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**TypeScript** - це надбудова над JavaScript, яка додає статичні типи і виявляє помилки ще до запуску коду. ## Теорія ### TL;DR - JavaScript падає в runtime, коли передаєш дані не того типу; TypeScript показує помилку в редакторі ще під час написання - Аналогія: JavaScript - це виписати чек без перевірки балансу; TypeScript - банківський застосунок, який блокує овердрафт до підтвердження - TypeScript компілюється в звичайний JavaScript, тому браузери і Node.js запускають його без змін - Використовуй TS для команд від 5+ людей або проєктів від 10k рядків; для швидких скриптів JS достатньо ### Швидкий приклад ```typescript // JavaScript: жодного попередження, помилка тільки в runtime let userAge = 25; userAge = "25"; console.log(userAge.toFixed(0)); // TypeError: userAge.toFixed is not a function // TypeScript: редактор показує проблему одразу let userAge: number = 25; userAge = "25"; // Помилка: Type 'string' is not assignable to type 'number' console.log(userAge.toFixed(0)); // Компілятор до цього рядка не дійде ``` У JS-версії баг живе непомітно до прод-деплою. TypeScript переміщує цей момент в редактор, до будь-якого шипінгу. ### Головна різниця JavaScript перевіряє типи в момент виконання. TypeScript додає структурну перевірку типів (structural type checking) ще на етапі написання, тому IDE одразу сигналізує про невідповідність. За даними команди TypeScript в Microsoft, це скорочує кількість багів на 15-20% у великих проєктах. Runtime-паніки стають помітками в редакторі. ### Коли використовувати - Прототип до 1k рядків, пишеш сам - JS, накладні витрати на налаштування не виправдані - Командний проєкт або React/Vue/Next.js застосунок - TS, ловить неправильні props до браузера - Node.js API з валідацією запитів - TS, перевіряє структуру даних до звернення в базу - Міграція легасі JS-коду - поступовий перехід з `allowJs: true`, без переписування всього підряд - Швидкий скрипт - JS, ітерація швидша ### Як працює компілятор Компілятор TypeScript (`tsc`) парсить `.ts`-файли, перевіряє типи за структурою (якщо форми об'єктів збігаються - типи сумісні), потім генерує звичайні `.js`-файли під ES5 або ES6. У VSCode TypeScript Language Service працює у фоні і дає діагностику в реальному часі без повної компіляції. Типи повністю зникають з вихідного коду. Node.js і браузери їх не бачать. ### Типові помилки **Помилка 1: `any` скрізь** ```typescript // Неправильно: зводить нанівець весь сенс TypeScript function processData(data: any) { return data.foo.bar; // Runtime-краш, як і в чистому JS } // Правильно: використовуй unknown і звужуй тип явно function processData(data: unknown) { if (typeof data === 'object' && data !== null && 'foo' in data) { return (data as { foo: { bar: string } }).foo.bar; } } ``` `any` вимикає всю перевірку типів для цього значення. TypeScript нічим не допомагає. Використовуй `unknown` замість нього. **Помилка 2: `strictNullChecks` вимкнений** ```typescript // Неправильно (strictNullChecks: false у tsconfig) let user = getUser(); user.name.toUpperCase(); // Падіння, якщо user дорівнює null // Правильно (strictNullChecks: true) let user = getUser(); user?.name?.toUpperCase(); // Безпечно ``` Без `strictNullChecks` TypeScript дозволяє передавати `null` і `undefined` куди завгодно. Більшість runtime-помилок лишається прихованою. Вмикай strict-режим з самого початку. **Помилка 3: Не розуміти різниці між `interface` і `type`** ```typescript // interface: підтримує злиття декларацій (declaration merging) interface User { name: string } interface User { age: number } // Автоматично об'єднується: { name: string; age: number } // type: злиття немає, підходить для union і intersection type Status = 'active' | 'inactive'; type AdminUser = User & { role: 'admin' }; ``` Багато команд обирають `interface` для структур об'єктів і `type` для union-типів та композицій. Обидва варіанти працюють. Різниця стає відчутною, коли треба розширювати або комбінувати типи. ### Де зустрічається в реальному коді - **React / Next.js** - типізовані props через `interface Props { user: User }` - **Express / NestJS** - DTO-класи для валідації структури запиту до звернення в базу - **Redux Toolkit** - `PayloadAction<User>` робить структуру action-ів передбачуваною - **Node.js** - `@types/node` надає типи для стандартної бібліотеки Node ### Питання для співбесіди **Q:** Що таке structural typing у TypeScript? **A:** Два типи сумісні, якщо їхні структури збігаються, незалежно від назви. Об'єкт з `{ x: number }` підходить для будь-якого типу, що вимагає `{ x: number }`, навіть без явного оголошення. **Q:** Як TypeScript працює з JS-бібліотеками без типів? **A:** Через пакети `@types/`, наприклад `@types/react` або `@types/node`. Це `.d.ts`-файли, які описують форму бібліотеки без TypeScript-коду в самій бібліотеці. **Q:** Чи варто завжди вмикати strict-режим? **A:** Так, в нових проєктах. `strict: true` у `tsconfig.json` вмикає `strictNullChecks`, `noImplicitAny` та ще кілька перевірок, які ловлять найпоширеніші баги. Без них TypeScript - тільки назва. **Q:** Як мігрувати JS-проєкт на TypeScript без поломки CI? **A:** Встав `allowJs: true` і `checkJs: false` у `tsconfig.json`. Додавай `// @ts-check` в окремі файли поступово. Так `.js` і `.ts` файли спокійно співіснують під час переходу. ## Приклади ### Типізований React-компонент ```typescript interface UserProps { id: number; name: string; isAdmin: boolean; } const UserCard: React.FC<UserProps> = ({ id, name, isAdmin }) => ( <div> {name} (ID: {id}){isAdmin && <strong> Admin</strong>} </div> ); // TypeScript ловить це до завантаження в браузері: // <UserCard id="1" name="Alice" isAdmin={false} /> // Помилка: Type 'string' is not assignable to type 'number' ``` Передати `id="1"` (рядок) замість `id={1}` (число) - один з найпоширеніших багів у React. TypeScript блокує це на етапі компіляції. Такий патерн зустрічається в кожному Next.js або Remix проєкті з типізованими компонентами. ### Discriminated union в API-обробнику ```typescript type ApiResponse = | { success: true; data: string } | { success: false; error: string }; function handleUser(id: number): ApiResponse { if (id > 0) { return { success: true, data: 'User found' }; } return { success: false, error: 'Invalid ID' }; } const result = handleUser(1); if (result.success) { console.log(result.data); // TypeScript знає: тут є 'data' } else { console.log(result.error); // А тут є 'error' } ``` Поле `success` виступає дискримінатором (discriminator). TypeScript звужує тип всередині кожної гілки, тому доступ до `.data` у гілці помилки - це помилка компіляції, а не runtime-сюрприз. Цей підхід часто зустрічається в NestJS і Express обробниках. Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.