Conditional type работает как тернарный оператор, но на уровне типов: «если тип T совместим с типом U — используй X, иначе Y».
type IsString<T> = T extends string ? 'да, строка' : 'нет, не строка'
type A = IsString<string> // 'да, строка'
type B = IsString<number> // 'нет, не строка'
type C = IsString<'hello'> // 'да, строка' — литеральный тип extends stringNonNullable<T> убирает null и undefined из типа:
// Реализация в lib.d.ts:
type NonNullable<T> = T extends null | undefined ? never : T
type A = NonNullable<string | null> // string
type B = NonNullable<number | undefined> // number
type C = NonNullable<null> // neverКогда T — union type, conditional type **распределяется** по каждому члену union:
type ToArray<T> = T extends any ? T[] : never
type A = ToArray<string | number>
// Распределяется как:
// ToArray<string> | ToArray<number>
// = string[] | number[]
// Чтобы отключить дистрибутивность — оберни в []
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never
type B = ToArrayNonDist<string | number> // (string | number)[]infer позволяет «захватить» тип внутри extends и использовать его:
// ReturnType<T> — стандартный utility type, реализован через infer:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never
type A = ReturnType<() => string> // string
type B = ReturnType<(x: number) => void> // void
type C = ReturnType<typeof JSON.parse> // any
// Извлечение типа элемента массива:
type ElementType<T> = T extends (infer E)[] ? E : never
type D = ElementType<string[]> // string
type E = ElementType<number[][]> // number[]
// Извлечение типа Promise:
type Awaited<T> = T extends Promise<infer R> ? Awaited<R> : T
// (рекурсивно разворачивает вложенные Promise)
type F = Awaited<Promise<string>> // string
type G = Awaited<Promise<Promise<number>>> // numbertype DeepReadonly<T> = T extends (infer E)[]
? ReadonlyArray<DeepReadonly<E>>
: T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T
interface Config {
server: { host: string; port: number }
flags: string[]
}
type ReadonlyConfig = DeepReadonly<Config>
// {
// readonly server: { readonly host: string; readonly port: number }
// readonly flags: ReadonlyArray<string>
// }// Проверка — является ли тип функцией
type IsFunction<T> = T extends (...args: any[]) => any ? true : false
type A = IsFunction<() => void> // true
type B = IsFunction<string> // false
// Unpromisify — убирает обёртку Promise
type Unpromisify<T> = T extends Promise<infer R> ? R : T
// Parameters<T> — типы параметров функции
type Parameters<T> = T extends (...args: infer P) => any ? P : never
type Params = Parameters<(a: string, b: number) => void>
// [string, number]
// ConstructorParameters<T> — параметры конструктора
type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : neverRuntime аналоги conditional types: deepFreeze, deepClone, deepMerge — условные преобразования объектов в JS
// В TypeScript conditional types работают на уровне системы типов.
// В JavaScript мы реализуем те же идеи как runtime-функции,
// которые условно преобразуют объекты в зависимости от их структуры.
// deepFreeze — рекурсивно замораживает объект (аналог DeepReadonly<T>)
function deepFreeze(obj) {
if (obj === null || typeof obj !== 'object') return obj
// Сначала замораживаем все вложенные объекты
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object' && obj[key] !== null) {
deepFreeze(obj[key])
}
})
return Object.freeze(obj)
}
// deepClone — глубокое копирование (аналог работы с immutable типами)
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj
if (Array.isArray(obj)) return obj.map(deepClone)
const cloned = {}
Object.keys(obj).forEach(key => {
cloned[key] = deepClone(obj[key])
})
return cloned
}
// deepMerge — слияние двух объектов (второй перекрывает первый)
function deepMerge(base, override) {
if (typeof base !== 'object' || base === null) return override
if (typeof override !== 'object' || override === null) return override
const result = deepClone(base)
Object.keys(override).forEach(key => {
if (
typeof override[key] === 'object' &&
override[key] !== null &&
typeof result[key] === 'object' &&
result[key] !== null
) {
result[key] = deepMerge(result[key], override[key])
} else {
result[key] = override[key]
}
})
return result
}
// isFrozen — проверяет что объект и все вложенные заморожены
function isFrozen(obj) {
if (obj === null || typeof obj !== 'object') return true
if (!Object.isFrozen(obj)) return false
return Object.keys(obj).every(key => isFrozen(obj[key]))
}
// --- Демонстрация ---
const config = {
server: { host: 'localhost', port: 3000 },
db: { host: 'localhost', port: 5432, credentials: { user: 'admin' } }
}
console.log('=== deepFreeze ===')
const frozenConfig = deepFreeze(deepClone(config))
console.log('Объект заморожен:', isFrozen(frozenConfig)) // true
console.log('Вложенный заморожен:', isFrozen(frozenConfig.server)) // true
// Попытка изменить замороженный объект молча проигнорируется
frozenConfig.server.port = 9999
console.log('port после попытки изменения:', frozenConfig.server.port) // 3000
console.log('\n=== deepClone ===')
const original = { a: 1, nested: { b: 2, arr: [1, 2, 3] } }
const clone = deepClone(original)
clone.nested.b = 999
clone.nested.arr.push(4)
console.log('Оригинал не изменился:', original.nested.b) // 2
console.log('Клон изменён:', clone.nested.b) // 999
console.log('Массив оригинала:', original.nested.arr) // [1, 2, 3]
console.log('\n=== deepMerge ===')
const defaults = {
theme: 'light',
settings: { lang: 'ru', timeout: 5000, debug: false }
}
const userPrefs = {
theme: 'dark',
settings: { lang: 'en', debug: true }
}
const merged = deepMerge(defaults, userPrefs)
console.log('theme:', merged.theme) // 'dark'
console.log('lang:', merged.settings.lang) // 'en'
console.log('timeout:', merged.settings.timeout) // 5000 (из defaults)
console.log('debug:', merged.settings.debug) // true (из userPrefs)Conditional type работает как тернарный оператор, но на уровне типов: «если тип T совместим с типом U — используй X, иначе Y».
type IsString<T> = T extends string ? 'да, строка' : 'нет, не строка'
type A = IsString<string> // 'да, строка'
type B = IsString<number> // 'нет, не строка'
type C = IsString<'hello'> // 'да, строка' — литеральный тип extends stringNonNullable<T> убирает null и undefined из типа:
// Реализация в lib.d.ts:
type NonNullable<T> = T extends null | undefined ? never : T
type A = NonNullable<string | null> // string
type B = NonNullable<number | undefined> // number
type C = NonNullable<null> // neverКогда T — union type, conditional type **распределяется** по каждому члену union:
type ToArray<T> = T extends any ? T[] : never
type A = ToArray<string | number>
// Распределяется как:
// ToArray<string> | ToArray<number>
// = string[] | number[]
// Чтобы отключить дистрибутивность — оберни в []
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never
type B = ToArrayNonDist<string | number> // (string | number)[]infer позволяет «захватить» тип внутри extends и использовать его:
// ReturnType<T> — стандартный utility type, реализован через infer:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never
type A = ReturnType<() => string> // string
type B = ReturnType<(x: number) => void> // void
type C = ReturnType<typeof JSON.parse> // any
// Извлечение типа элемента массива:
type ElementType<T> = T extends (infer E)[] ? E : never
type D = ElementType<string[]> // string
type E = ElementType<number[][]> // number[]
// Извлечение типа Promise:
type Awaited<T> = T extends Promise<infer R> ? Awaited<R> : T
// (рекурсивно разворачивает вложенные Promise)
type F = Awaited<Promise<string>> // string
type G = Awaited<Promise<Promise<number>>> // numbertype DeepReadonly<T> = T extends (infer E)[]
? ReadonlyArray<DeepReadonly<E>>
: T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T
interface Config {
server: { host: string; port: number }
flags: string[]
}
type ReadonlyConfig = DeepReadonly<Config>
// {
// readonly server: { readonly host: string; readonly port: number }
// readonly flags: ReadonlyArray<string>
// }// Проверка — является ли тип функцией
type IsFunction<T> = T extends (...args: any[]) => any ? true : false
type A = IsFunction<() => void> // true
type B = IsFunction<string> // false
// Unpromisify — убирает обёртку Promise
type Unpromisify<T> = T extends Promise<infer R> ? R : T
// Parameters<T> — типы параметров функции
type Parameters<T> = T extends (...args: infer P) => any ? P : never
type Params = Parameters<(a: string, b: number) => void>
// [string, number]
// ConstructorParameters<T> — параметры конструктора
type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : neverRuntime аналоги conditional types: deepFreeze, deepClone, deepMerge — условные преобразования объектов в JS
// В TypeScript conditional types работают на уровне системы типов.
// В JavaScript мы реализуем те же идеи как runtime-функции,
// которые условно преобразуют объекты в зависимости от их структуры.
// deepFreeze — рекурсивно замораживает объект (аналог DeepReadonly<T>)
function deepFreeze(obj) {
if (obj === null || typeof obj !== 'object') return obj
// Сначала замораживаем все вложенные объекты
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object' && obj[key] !== null) {
deepFreeze(obj[key])
}
})
return Object.freeze(obj)
}
// deepClone — глубокое копирование (аналог работы с immutable типами)
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj
if (Array.isArray(obj)) return obj.map(deepClone)
const cloned = {}
Object.keys(obj).forEach(key => {
cloned[key] = deepClone(obj[key])
})
return cloned
}
// deepMerge — слияние двух объектов (второй перекрывает первый)
function deepMerge(base, override) {
if (typeof base !== 'object' || base === null) return override
if (typeof override !== 'object' || override === null) return override
const result = deepClone(base)
Object.keys(override).forEach(key => {
if (
typeof override[key] === 'object' &&
override[key] !== null &&
typeof result[key] === 'object' &&
result[key] !== null
) {
result[key] = deepMerge(result[key], override[key])
} else {
result[key] = override[key]
}
})
return result
}
// isFrozen — проверяет что объект и все вложенные заморожены
function isFrozen(obj) {
if (obj === null || typeof obj !== 'object') return true
if (!Object.isFrozen(obj)) return false
return Object.keys(obj).every(key => isFrozen(obj[key]))
}
// --- Демонстрация ---
const config = {
server: { host: 'localhost', port: 3000 },
db: { host: 'localhost', port: 5432, credentials: { user: 'admin' } }
}
console.log('=== deepFreeze ===')
const frozenConfig = deepFreeze(deepClone(config))
console.log('Объект заморожен:', isFrozen(frozenConfig)) // true
console.log('Вложенный заморожен:', isFrozen(frozenConfig.server)) // true
// Попытка изменить замороженный объект молча проигнорируется
frozenConfig.server.port = 9999
console.log('port после попытки изменения:', frozenConfig.server.port) // 3000
console.log('\n=== deepClone ===')
const original = { a: 1, nested: { b: 2, arr: [1, 2, 3] } }
const clone = deepClone(original)
clone.nested.b = 999
clone.nested.arr.push(4)
console.log('Оригинал не изменился:', original.nested.b) // 2
console.log('Клон изменён:', clone.nested.b) // 999
console.log('Массив оригинала:', original.nested.arr) // [1, 2, 3]
console.log('\n=== deepMerge ===')
const defaults = {
theme: 'light',
settings: { lang: 'ru', timeout: 5000, debug: false }
}
const userPrefs = {
theme: 'dark',
settings: { lang: 'en', debug: true }
}
const merged = deepMerge(defaults, userPrefs)
console.log('theme:', merged.theme) // 'dark'
console.log('lang:', merged.settings.lang) // 'en'
console.log('timeout:', merged.settings.timeout) // 5000 (из defaults)
console.log('debug:', merged.settings.debug) // true (из userPrefs)Реализуй `deepFreeze(obj)` — рекурсивно замораживает объект и все вложенные объекты через Object.freeze. Реализуй `isFrozen(obj)` — проверяет что объект и все вложенные объекты заморожены (включая массивы и вложенные объекты произвольной глубины).
В deepFreeze: сначала рекурсивно обойди все значения-объекты через forEach, потом вызови Object.freeze(obj). В isFrozen: используй Object.isFrozen() для текущего объекта, затем every() для проверки всех вложенных значений рекурсивно.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке