Skip to main content

Що таке "conditional type"?

Що таке 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

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.