Когда вы применяете conditional type к generic-параметру, TypeScript автоматически «распределяет» его по каждому члену union-типа:
type ToArray<T> = T extends any ? T[] : never
// С обычным типом:
type A = ToArray<string> // string[]
// С union — распределяется автоматически!
type B = ToArray<string | number>
// = ToArray<string> | ToArray<number>
// = string[] | number[]Это называется **дистрибутивностью** — conditional type распределяется по членам union.
// Оставляем только те типы, которые extends U
type Filter<T, U> = T extends U ? T : never
type Strings = Filter<string | number | boolean | null, string>
// = string (остальные → never, never объединяется с ничем)
type NonNullable<T> = T extends null | undefined ? never : T
type A = NonNullable<string | null | undefined> // stringОберните T в кортеж [T], чтобы предотвратить распределение:
// С дистрибутивностью:
type IsUnion<T> = T extends any ? [T] : never
type A = IsUnion<string | number> // [string] | [number] — распределилось
// Без дистрибутивности — сравниваем union как целое:
type IsNeverDist<T> = T extends never ? true : false
type IsNeverNoDist<T> = [T] extends [never] ? true : false
type X = IsNeverDist<never> // never (дистрибутивность по never даёт never!)
type Y = IsNeverNoDist<never> // true — правильно// Убрать из union все функциональные типы
type NonFunction<T> = T extends (...args: any[]) => any ? never : T
type Clean = NonFunction<string | number | (() => void) | boolean>
// string | number | boolean
// Обернуть каждый тип в Promise
type Promisify<T> = T extends any ? Promise<T> : never
type P = Promisify<string | number> // Promise<string> | Promise<number>
// Получить типы значений объекта
type ValueOf<T> = T extends Record<string, infer V> ? V : never
// Оba варианта выборки ключей
type KeysOfType<T, V> = {
[K in keyof T]: T[K] extends V ? K : never
}[keyof T]
interface Form {
name: string
age: number
email: string
active: boolean
}
type StringKeys = KeysOfType<Form, string> // 'name' | 'email'// Разворачиваем вложенные Promise
type DeepAwaited<T> = T extends Promise<infer R>
? DeepAwaited<R>
: T
type A = DeepAwaited<Promise<Promise<string>>> // string
// Flatten — вытаскиваем тип из массива
type Flatten<T> = T extends Array<infer E> ? E : T
type B = Flatten<string[]> // string
type C = Flatten<number[][][]> // number[][] (один уровень)
// Рекурсивный Flatten:
type DeepFlatten<T> = T extends Array<infer E> ? DeepFlatten<E> : T
type D = DeepFlatten<number[][][]> // number// Эти типы реализованы через дистрибутивные conditional types:
type NonNullable<T> = T extends null | undefined ? never : T
type Extract<T, U> = T extends U ? T : never
type Exclude<T, U> = T extends U ? never : TRuntime аналоги дистрибутивных типов: функции для фильтрации и трансформации наборов значений
// TypeScript conditional types работают на уровне типов.
// В JS реализуем те же идеи как функции над массивами значений.
// Аналог: type Filter<T, U> = T extends U ? T : never
// Runtime-версия: фильтрация по предикату типа
function filterByType(values, typePredicate) {
return values.filter(typePredicate)
}
// Аналог: type NonNullable<T> = T excludes null | undefined
function removeNullable(values) {
return values.filter(v => v != null)
}
// Аналог: type Promisify<T> — оборачиваем значения
function promisifyAll(values) {
return values.map(v => Promise.resolve(v))
}
// Аналог: type Extract<T, U> = T extends U ? T : never
// Оставляем только значения определённого типа
function extract(values, type) {
return values.filter(v => typeof v === type || (type === 'array' && Array.isArray(v)))
}
// Аналог: type Exclude<T, U> = T extends U ? never : T
function exclude(values, type) {
return values.filter(v => typeof v !== type)
}
// Аналог DeepFlatten — рекурсивно разворачиваем массивы
function deepFlatten(arr) {
return arr.reduce((acc, item) => {
if (Array.isArray(item)) {
return acc.concat(deepFlatten(item))
}
return acc.concat(item)
}, [])
}
// Аналог Awaited — ждём все Promise в union
async function resolveAll(values) {
return Promise.all(values.map(v =>
v instanceof Promise ? v : Promise.resolve(v)
))
}
// --- Демонстрация ---
const mixed = [1, 'hello', null, true, undefined, 42, 'world', null, false]
console.log('=== NonNullable ===')
const noNulls = removeNullable(mixed)
console.log('Без null/undefined:', noNulls)
console.log('\n=== Extract (только строки) ===')
const strings = extract(mixed, 'string')
console.log('Строки:', strings)
console.log('\n=== Exclude (убрать числа) ===')
const noNumbers = exclude(mixed, 'number')
console.log('Без чисел:', noNumbers)
console.log('\n=== DeepFlatten ===')
const nested = [1, [2, 3], [4, [5, [6, 7]]]]
console.log('Вложенный:', JSON.stringify(nested))
console.log('После flatten:', deepFlatten(nested))
console.log('\n=== Union фильтрация (filterByType) ===')
const types = [
{ type: 'admin', name: 'Алексей' },
{ type: 'user', name: 'Ольга' },
{ type: 'admin', name: 'Иван' },
{ type: 'moderator',name: 'Мария' },
]
const admins = filterByType(types, t => t.type === 'admin')
console.log('Администраторы:', admins.map(t => t.name))
console.log('\n=== Awaited (Promise union) ===')
const maybePromises = [
Promise.resolve(1),
42,
Promise.resolve('hello'),
'world',
]
resolveAll(maybePromises).then(results => {
console.log('Resolved:', results)
})Когда вы применяете conditional type к generic-параметру, TypeScript автоматически «распределяет» его по каждому члену union-типа:
type ToArray<T> = T extends any ? T[] : never
// С обычным типом:
type A = ToArray<string> // string[]
// С union — распределяется автоматически!
type B = ToArray<string | number>
// = ToArray<string> | ToArray<number>
// = string[] | number[]Это называется **дистрибутивностью** — conditional type распределяется по членам union.
// Оставляем только те типы, которые extends U
type Filter<T, U> = T extends U ? T : never
type Strings = Filter<string | number | boolean | null, string>
// = string (остальные → never, never объединяется с ничем)
type NonNullable<T> = T extends null | undefined ? never : T
type A = NonNullable<string | null | undefined> // stringОберните T в кортеж [T], чтобы предотвратить распределение:
// С дистрибутивностью:
type IsUnion<T> = T extends any ? [T] : never
type A = IsUnion<string | number> // [string] | [number] — распределилось
// Без дистрибутивности — сравниваем union как целое:
type IsNeverDist<T> = T extends never ? true : false
type IsNeverNoDist<T> = [T] extends [never] ? true : false
type X = IsNeverDist<never> // never (дистрибутивность по never даёт never!)
type Y = IsNeverNoDist<never> // true — правильно// Убрать из union все функциональные типы
type NonFunction<T> = T extends (...args: any[]) => any ? never : T
type Clean = NonFunction<string | number | (() => void) | boolean>
// string | number | boolean
// Обернуть каждый тип в Promise
type Promisify<T> = T extends any ? Promise<T> : never
type P = Promisify<string | number> // Promise<string> | Promise<number>
// Получить типы значений объекта
type ValueOf<T> = T extends Record<string, infer V> ? V : never
// Оba варианта выборки ключей
type KeysOfType<T, V> = {
[K in keyof T]: T[K] extends V ? K : never
}[keyof T]
interface Form {
name: string
age: number
email: string
active: boolean
}
type StringKeys = KeysOfType<Form, string> // 'name' | 'email'// Разворачиваем вложенные Promise
type DeepAwaited<T> = T extends Promise<infer R>
? DeepAwaited<R>
: T
type A = DeepAwaited<Promise<Promise<string>>> // string
// Flatten — вытаскиваем тип из массива
type Flatten<T> = T extends Array<infer E> ? E : T
type B = Flatten<string[]> // string
type C = Flatten<number[][][]> // number[][] (один уровень)
// Рекурсивный Flatten:
type DeepFlatten<T> = T extends Array<infer E> ? DeepFlatten<E> : T
type D = DeepFlatten<number[][][]> // number// Эти типы реализованы через дистрибутивные conditional types:
type NonNullable<T> = T extends null | undefined ? never : T
type Extract<T, U> = T extends U ? T : never
type Exclude<T, U> = T extends U ? never : TRuntime аналоги дистрибутивных типов: функции для фильтрации и трансформации наборов значений
// TypeScript conditional types работают на уровне типов.
// В JS реализуем те же идеи как функции над массивами значений.
// Аналог: type Filter<T, U> = T extends U ? T : never
// Runtime-версия: фильтрация по предикату типа
function filterByType(values, typePredicate) {
return values.filter(typePredicate)
}
// Аналог: type NonNullable<T> = T excludes null | undefined
function removeNullable(values) {
return values.filter(v => v != null)
}
// Аналог: type Promisify<T> — оборачиваем значения
function promisifyAll(values) {
return values.map(v => Promise.resolve(v))
}
// Аналог: type Extract<T, U> = T extends U ? T : never
// Оставляем только значения определённого типа
function extract(values, type) {
return values.filter(v => typeof v === type || (type === 'array' && Array.isArray(v)))
}
// Аналог: type Exclude<T, U> = T extends U ? never : T
function exclude(values, type) {
return values.filter(v => typeof v !== type)
}
// Аналог DeepFlatten — рекурсивно разворачиваем массивы
function deepFlatten(arr) {
return arr.reduce((acc, item) => {
if (Array.isArray(item)) {
return acc.concat(deepFlatten(item))
}
return acc.concat(item)
}, [])
}
// Аналог Awaited — ждём все Promise в union
async function resolveAll(values) {
return Promise.all(values.map(v =>
v instanceof Promise ? v : Promise.resolve(v)
))
}
// --- Демонстрация ---
const mixed = [1, 'hello', null, true, undefined, 42, 'world', null, false]
console.log('=== NonNullable ===')
const noNulls = removeNullable(mixed)
console.log('Без null/undefined:', noNulls)
console.log('\n=== Extract (только строки) ===')
const strings = extract(mixed, 'string')
console.log('Строки:', strings)
console.log('\n=== Exclude (убрать числа) ===')
const noNumbers = exclude(mixed, 'number')
console.log('Без чисел:', noNumbers)
console.log('\n=== DeepFlatten ===')
const nested = [1, [2, 3], [4, [5, [6, 7]]]]
console.log('Вложенный:', JSON.stringify(nested))
console.log('После flatten:', deepFlatten(nested))
console.log('\n=== Union фильтрация (filterByType) ===')
const types = [
{ type: 'admin', name: 'Алексей' },
{ type: 'user', name: 'Ольга' },
{ type: 'admin', name: 'Иван' },
{ type: 'moderator',name: 'Мария' },
]
const admins = filterByType(types, t => t.type === 'admin')
console.log('Администраторы:', admins.map(t => t.name))
console.log('\n=== Awaited (Promise union) ===')
const maybePromises = [
Promise.resolve(1),
42,
Promise.resolve('hello'),
'world',
]
resolveAll(maybePromises).then(results => {
console.log('Resolved:', results)
})Реализуй функцию `partition(array, predicate)` — разбивает массив на два: первый содержит элементы, для которых predicate возвращает true, второй — false. Возвращает кортеж [truthy[], falsy[]]. Реализуй функцию `groupByType(values)` — группирует значения из массива по типу (typeof), возвращает объект вида { string: [...], number: [...], boolean: [...], ... }. Нулевые значения (null) должны попасть в группу "null".
partition: const yes = array.filter(predicate); const no = array.filter(x => !predicate(x)); return [yes, no]. groupByType: const groups = {}; for (const v of values) { const key = v === null ? "null" : typeof v; if (!groups[key]) groups[key] = []; groups[key].push(v) } return groups.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке