Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке "conditional type"?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Conditional Type** (умовний тип) має загальну форму `T extends U ? X : Y`: якщо тип `T` **сумісний** (assignable) з типом `U`, результатом буде тип `X`, інакше - тип `Y`. **Ключове:** умова обчислюється **на етапі компіляції**, а результатом є **інший тип**, а не значення.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Що таке Conditional Type **Conditional Type** (умовний тип) має загальну форму: ```javascript T extends U ? X : Y ``` Це читається як: > "Якщо тип `T` **сумісний** (assignable) з типом `U`, > то результатом буде тип `X`, інакше - тип `Y`." --- ### Найпростіший приклад ```javascript type IsString<T> = T extends string ? "yes" : "no"; type A = IsString<string>; // "yes" type B = IsString<number>; // "no" ``` --- ## Як це працює - `extends` у контексті conditional type - не "успадкування", а **перевірка сумісності типів**; - умова обчислюється **на етапі компіляції**; - результат - **інший тип**. --- ## Приклад з реальними типами ```javascript type ElementType<T> = T extends (infer U)[] ? U : T; type A = ElementType<string[]>; // string type B = ElementType<number[]>; // number type C = ElementType<boolean>; // boolean ``` > Тут використовується `infer U` - оператор для **видобування** підтипу. --- ## Використання з узагальненнями (generics) Conditional types особливо потужні в **дженериках**: ```javascript type Response<T> = T extends Error ? "Failed" : "Success"; type A = Response<string>; // "Success" type B = Response<Error>; // "Failed" ``` --- ## Приклад: фільтрація типів ```javascript type NonNullable<T> = T extends null | undefined ? never : T; type A = NonNullable<string | null | undefined>; // string ``` > Це **реальна реалізація** вбудованого `NonNullable`. --- ## Дистрибутивна поведінка Conditional types поводяться **по-різному**, якщо `T` - це **union**. Якщо `T` - об'єднання (`A | B`), TypeScript **розподіляє** умову на кожну частину: ```javascript type ToArray<T> = T extends any ? T[] : never; type R = ToArray<string | number>; // (string | number) розподіляється -> string[] | number[] ``` Тобто: ```javascript ToArray<string | number> ≡ ToArray<string> | ToArray<number> ``` Це називається **distributive conditional type**. --- ### Щоб вимкнути розподіл Можна **загорнути тип у кортеж**, щоб "зняти" дистрибутивність: ```javascript type ToArrayNonDist<T> = [T] extends [any] ? T[] : never; type R = ToArrayNonDist<string | number>; // (string | number)[] ``` --- ## Приклади зі стандартної бібліотеки Багато вбудованих утиліт побудовані на conditional types: | Utility | Реалізація | Що робить | |---|---|---| | `Exclude<T, U>` | `T extends U ? never : T` | Прибирає з `T` усе, що сумісне з `U` | | `Extract<T, U>` | `T extends U ? T : never` | Залишає лише перетин `T` і `U` | | `NonNullable<T>` | `T extends null \| undefined ? never : T` | Прибирає `null` і `undefined` | | `ReturnType<T>` | `T extends (...args:any) => infer R ? R : any` | Видобуває тип значення, що повертається | | `InstanceType<T>` | `T extends new (...args:any) => infer R ? R : any` | Видобуває тип екземпляра, що створюється | --- ## Приклади з практики ### Вибір типу, що повертається ```javascript type Result<T> = T extends true ? "OK" : "FAIL"; type A = Result<true>; // "OK" type B = Result<false>; // "FAIL" ``` --- ### Перевірка, чи є тип функцією ```javascript type IsFunction<T> = T extends (...args: any[]) => any ? true : false; type A = IsFunction<() => void>; // true type B = IsFunction<number>; // false ``` --- ### Видобування результату Promise ```javascript type UnwrapPromise<T> = T extends Promise<infer U> ? U : T; type A = UnwrapPromise<Promise<string>>; // string type B = UnwrapPromise<number>; // number ``` --- ### Умовна типізація параметрів ```javascript type ApiResponse<T> = T extends { error: any } ? { ok: false; error: T["error"] } : { ok: true; data: T }; type A = ApiResponse<{ data: string }>; // { ok: true; data: { data: string } } type B = ApiResponse<{ error: string }>; // { ok: false; error: string } ``` --- ## Ключові концепції | Термін | Пояснення | |---|---| | `extends` | Перевіряє сумісність типів | | `infer` | Дозволяє видобувати вкладені типи | | Дистрибутивність | Conditional-тип застосовується окремо до кожного члена union | | `never` | Використовується для "виключення" значень з union | | "Type-level if" | Conditional types - спосіб описувати розгалуження логіки на рівні типів | --- ## Підсумок | Поняття | Опис | |---|---| | **Conditional type** | Тип, який обирається залежно від іншого типу | | **Синтаксис** | `T extends U ? X : Y` | | **Коли використовувати** | Коли тип залежить від форми/сумісності іншого типу | | **Ключові інструменти** | `extends`, `infer`, `never` | | **Приклади** | `Exclude`, `Extract`, `ReturnType`, `NonNullable`, `InstanceType` |Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.