Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Утилітарний тип partial у TypeScript». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`Partial<T>`** - утилітарний тип TypeScript, який робить кожну властивість T необов'язковою на етапі компіляції. ```ts type User = { id: number; name: string }; type PartialUser = Partial<User>; // { id?: number; name?: string } function patch(user: User, changes: Partial<User>): User { return { ...user, ...changes }; } ``` **Ключове:** працює лише на першому рівні. Вкладені об'єкти залишаються повністю обов'язковими, якщо їх передати.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`Partial<T>`** - вбудований утилітарний тип TypeScript, який перетворює кожну властивість об'єктного типу T на необов'язкову. ## Теорія ### TL;DR - Уяви чернетку: заповнюєш тільки поля, які вже є, решту пропускаєш - `{ name: string }` стає `{ name?: string }` для кожної властивості T - Підходить для PATCH-запитів, стану форм і конфігураційних об'єктів - Працює лише на першому рівні. Вкладені об'єкти залишаються обов'язковими, якщо їх передати - Жодного впливу на runtime. TypeScript видаляє тип під час компіляції ### Швидкий приклад ```ts type User = { id: number; name: string; email: string; }; // Всі поля стають необов'язковими const update: Partial<User> = { name: "Alice" }; // id та email пропущені - ок // Патерн злиття const fullUser: User = { id: 1, name: "Bob", email: "bob@test.com" }; const updated: User = { ...fullUser, ...update }; // { id: 1, name: "Alice", email: "bob@test.com" } ``` `update` містить лише поле, яке треба змінити. Spread об'єднує його з повним `User`. ### Головна відмінність `Partial<T>` масштабується разом з T автоматично. Додаєш нове поле до `User` - кожен `Partial<User>` у кодовій базі відображає це без жодних змін. Зручніше ніж вручну підтримувати `{ name?: string; email?: string }` і стежити за синхронізацією. ### Коли використовувати - PATCH-запити до API: надсилаєш серверу лише змінені поля - Стан форми в React: відстежуєш, які поля користувач вже заповнив - Конфігураційні об'єкти: задаєш дефолти і дозволяєш перевизначати частину - Оновлення в reducer: зливаєш частковий payload з поточним станом ### Як компілятор обробляє це TypeScript розгортає `Partial<T>` як mapped type: `{ [K in keyof T]?: T[K] }`. Компілятор проходить кожен ключ T і додає `?`. У JavaScript нічого не потрапляє. V8 і браузер про цей тип не знають. ### Типові помилки **Помилка 1: очікування рекурсивної роботи Partial.** ```ts type Nested = { user: { name: string; age: number }; }; const x: Partial<Nested> = { user: { name: "Alice" } }; // Помилка! age все ще обов'язковий ``` `Partial` торкається лише верхнього рівня. Якщо передаєш `user`, його внутрішня структура залишається повністю обов'язковою. Ця пастка спрацьовує майже завжди вперше. Для глибокої опціональності потрібен власний `DeepPartial`: ```ts type DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T; ``` **Помилка 2: забуті перевірки на undefined в runtime.** ```ts function update(data: Partial<User>) { console.log(data.id.toString()); // Компілюється. Падає якщо id відсутній. } ``` TypeScript не генерує жодних перевірок. Додавай сам: `if (data.id !== undefined)`. **Помилка 3: використовувати Partial коли одне поле завжди обов'язкове.** Якщо `id` завжди required, `Partial<User>` дозволяє його пропустити теж. Комбінуй типи: ```ts type PatchUser = Pick<User, "id"> & Partial<Omit<User, "id">>; ``` **Помилка 4: застосовувати Partial до примітиву.** ```ts type ID = number; const x: Partial<ID> = undefined; // Помилка. Partial<number> залишається number. ``` Примітиви не мають ключів. `Partial` на них не впливає. Використовуй `number | undefined`. ### Де зустрічається - **React** (React Hook Form): `useState<Partial<Profile>>({})` для часткового стану форми - **NestJS**: `Partial<CreateUserDto>` як тип body для PATCH-ендпоінтів - **Redux Toolkit**: `Partial<State>` в slice reducers для опціонального оновлення полів - **Prisma**: `prisma.user.update({ data: partial })` приймає часткову форму моделі ### Питання на співбесіді **Q:** У що розгортається `Partial<{ a: string; b: number }>`? **A:** У `{ a?: string; b?: number }`. Кожна властивість отримує `?`. **Q:** `Partial` зберігає модифікатор `readonly`? **A:** Ні. `Partial<{ readonly x: number }>` прибирає `readonly`, залишаючи `{ x?: number }` як записуване. **Q:** Чи є вплив на runtime-продуктивність? **A:** Жодного. Це чистий compile-time тип. JavaScript-вивід однаковий з `Partial` і без нього. **Q:** Яка різниця між `Partial<T>` і `{ [K in keyof T]?: T[K] }`? **A:** Вони ідентичні. `Partial<T>` - іменований псевдонім цього mapped type, визначений у `lib.es5.d.ts`. **Q:** (Senior) Як описати тип для PATCH-ендпоінту, де `id` завжди обов'язковий, а решта полів - ні? **A:** `type PatchUser = Pick<User, 'id'> & Partial<Omit<User, 'id'>>`. Це гарантує наявність `id` і робить решту опціональними. ## Приклади ### Базовий: оновлення об'єкта через spread ```ts type Point = { x: number; y: number; z?: number }; function move(origin: Point, delta: Partial<Point>): Point { return { ...origin, ...delta }; } const start: Point = { x: 0, y: 0 }; const result = move(start, { x: 10 }); // { x: 10, y: 0 } ``` `delta` містить лише координати, які треба змінити. `move` об'єднує їх з поточною позицією і повертає повний `Point`. ### Проміжний: стан форми в React ```ts interface Profile { name: string; age: number; bio: string; } function EditProfileForm({ user }: { user: Profile }) { const [draft, setDraft] = useState<Partial<Profile>>({}); const handleSave = () => { const updated: Profile = { ...user, ...draft }; // безпечне злиття saveProfile(updated); }; return ( <input value={draft.name ?? user.name} onChange={(e) => setDraft((d) => ({ ...d, name: e.target.value }))} /> ); } ``` `draft` зберігає лише поля, яких торкнувся користувач. При збереженні вони зливаються з оригінальним `user`, і жодне поле не зникає. Патерн працює з будь-якою бібліотекою форм або звичайним React state.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.