TypeScript предоставляет готовые утилиты для трансформации типов. Все они реализованы через mapped types и conditional types.
interface User {
id: number
name: string
email: string
age: number
}
// Все поля опциональны
type UserUpdate = Partial<User>
// { id?: number; name?: string; email?: string; age?: number }
// Все поля обязательны (убирает ?)
type UserRequired = Required<Partial<User>>
// { id: number; name: string; ... }
// Практика — обновление записи:
function updateUser(user: User, changes: Partial<User>): User {
return { ...user, ...changes }
}Как реализовано: type Partial<T> = { [K in keyof T]?: T[K] }
type ReadonlyUser = Readonly<User>
// Все поля readonly — нельзя изменить после создания
const user: ReadonlyUser = { id: 1, name: 'Алексей', email: 'a@b.ru', age: 30 }
// user.name = 'Другой' // Ошибка TS: Cannot assign to 'name' because it is a read-only propertyСоздаёт тип объекта с ключами K и значениями V:
type PageViews = Record<string, number>
const views: PageViews = { '/home': 100, '/about': 50 }
type UserRoles = Record<'admin' | 'user' | 'guest', string[]>
const roles: UserRoles = {
admin: ['read', 'write', 'delete'],
user: ['read', 'write'],
guest: ['read'],
}Реализация: type Record<K extends keyof any, V> = { [P in K]: V }
interface Article {
id: number
title: string
content: string
authorId: number
publishedAt: Date
tags: string[]
}
// Берём только нужные поля:
type ArticlePreview = Pick<Article, 'id' | 'title' | 'publishedAt'>
// Убираем ненужные поля:
type ArticleWithoutContent = Omit<Article, 'content'>
// Полезно для форм:
type CreateArticleForm = Omit<Article, 'id' | 'publishedAt'>Работают с union types:
type Status = 'pending' | 'success' | 'error' | 'cancelled'
// Убираем из union:
type ActiveStatus = Exclude<Status, 'cancelled'>
// 'pending' | 'success' | 'error'
// Оставляем только совпадающие:
type FinalStatus = Extract<Status, 'success' | 'error'>
// 'success' | 'error'
// NonNullable — убирает null и undefined:
type Name = string | null | undefined
type DefiniteName = NonNullable<Name> // stringfunction createUser(name: string, age: number): User {
return { id: Date.now(), name, email: '', age }
}
type UserType = ReturnType<typeof createUser> // User
type CreateParams = Parameters<typeof createUser> // [string, number]
class UserService {
findById(id: number): User { /* ... */ }
}
type ServiceInstance = InstanceType<typeof UserService>
// UserService — тип экземпляра класса// Partial реализован так:
type MyPartial<T> = {
[K in keyof T]?: T[K]
// K перебирает все ключи T
// ? делает поле опциональным
// T[K] — тип значения для ключа K
}
// Pick реализован так:
type MyPick<T, K extends keyof T> = {
[P in K]: T[P]
// Перебираем только ключи из K
}
// Record реализован так:
type MyRecord<K extends keyof any, V> = {
[P in K]: V
}interface Config {
apiUrl: string
timeout: number
retries: number
debug: boolean
}
// Дефолтные значения:
const defaults: Config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3,
debug: false,
}
// Пользователь передаёт только то что хочет изменить:
function createConfig(overrides: Partial<Config> = {}): Config {
return { ...defaults, ...overrides }
}
const config = createConfig({ debug: true, timeout: 10000 })
// { apiUrl: 'https://...', timeout: 10000, retries: 3, debug: true }Реализация pick, omit, merge как функции. Практичный пример: данные из API + merge с дефолтами через Partial-паттерн
// В TypeScript: Pick<T, keys>, Omit<T, keys>, Partial<T>
// В JS — реализуем как функции
function pick(obj, keys) {
return keys.reduce((acc, key) => {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
acc[key] = obj[key]
}
return acc
}, {})
}
function omit(obj, keys) {
const keySet = new Set(keys)
return Object.fromEntries(
Object.entries(obj).filter(([k]) => !keySet.has(k))
)
}
// Merge: накладываем partial поверх base (только существующие ключи)
function mergePartial(base, partial) {
const result = { ...base }
for (const key of Object.keys(partial)) {
if (Object.prototype.hasOwnProperty.call(base, key)) {
result[key] = partial[key]
}
}
return result
}
// Merge: сливаем все ключи (расширяющий merge)
function merge(base, partial) {
return { ...base, ...partial }
}
// Readonly-заморозка объекта (как Readonly<T>)
function readonly(obj) {
return Object.freeze({ ...obj })
}
// required — убеждаемся что все поля непустые
function assertRequired(obj, requiredKeys) {
const missing = requiredKeys.filter(k => obj[k] == null)
if (missing.length > 0) {
throw new Error(`Отсутствуют обязательные поля: ${missing.join(', ')}`)
}
return obj
}
// --- Практичный пример: API данные + дефолты ---
const userDefaults = {
id: null,
name: 'Аноним',
email: '',
age: 0,
role: 'user',
active: true,
createdAt: new Date().toISOString(),
}
// Данные пришли из API — могут быть неполными
const apiResponse = {
id: 42,
name: 'Алексей Петров',
email: 'alex@mail.ru',
}
// Применяем паттерн Partial<User>: только переданные поля перезаписывают дефолты
const fullUser = merge(userDefaults, apiResponse)
console.log('=== Полный объект пользователя ===')
console.log(fullUser)
// Pick — берём только нужные поля для карточки профиля
const profileCard = pick(fullUser, ['id', 'name', 'email', 'role'])
console.log('\n=== Карточка профиля (Pick) ===')
console.log(profileCard)
// Omit — убираем служебные поля перед отправкой
const publicUser = omit(fullUser, ['createdAt', 'active'])
console.log('\n=== Публичные данные (Omit) ===')
console.log(publicUser)
// Readonly — замораживаем конфигурацию
const config = readonly({
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3,
})
console.log('\n=== Readonly конфигурация ===')
console.log(config)
try {
config.timeout = 10000 // В strict mode — TypeError, иначе тихо игнорируется
} catch (e) {
console.log(`Нельзя изменить readonly: ${e.message}`)
}
// assertRequired — валидация
console.log('\n=== Валидация required полей ===')
try {
assertRequired({ name: 'Алексей', email: null }, ['name', 'email', 'id'])
} catch (e) {
console.log(`Ошибка: ${e.message}`) // 'Отсутствуют обязательные поля: email, id'
}
const validated = assertRequired(
{ name: 'Алексей', email: 'a@b.ru', id: 1 },
['name', 'email', 'id']
)
console.log(`Валидация прошла: ${validated.name}`)TypeScript предоставляет готовые утилиты для трансформации типов. Все они реализованы через mapped types и conditional types.
interface User {
id: number
name: string
email: string
age: number
}
// Все поля опциональны
type UserUpdate = Partial<User>
// { id?: number; name?: string; email?: string; age?: number }
// Все поля обязательны (убирает ?)
type UserRequired = Required<Partial<User>>
// { id: number; name: string; ... }
// Практика — обновление записи:
function updateUser(user: User, changes: Partial<User>): User {
return { ...user, ...changes }
}Как реализовано: type Partial<T> = { [K in keyof T]?: T[K] }
type ReadonlyUser = Readonly<User>
// Все поля readonly — нельзя изменить после создания
const user: ReadonlyUser = { id: 1, name: 'Алексей', email: 'a@b.ru', age: 30 }
// user.name = 'Другой' // Ошибка TS: Cannot assign to 'name' because it is a read-only propertyСоздаёт тип объекта с ключами K и значениями V:
type PageViews = Record<string, number>
const views: PageViews = { '/home': 100, '/about': 50 }
type UserRoles = Record<'admin' | 'user' | 'guest', string[]>
const roles: UserRoles = {
admin: ['read', 'write', 'delete'],
user: ['read', 'write'],
guest: ['read'],
}Реализация: type Record<K extends keyof any, V> = { [P in K]: V }
interface Article {
id: number
title: string
content: string
authorId: number
publishedAt: Date
tags: string[]
}
// Берём только нужные поля:
type ArticlePreview = Pick<Article, 'id' | 'title' | 'publishedAt'>
// Убираем ненужные поля:
type ArticleWithoutContent = Omit<Article, 'content'>
// Полезно для форм:
type CreateArticleForm = Omit<Article, 'id' | 'publishedAt'>Работают с union types:
type Status = 'pending' | 'success' | 'error' | 'cancelled'
// Убираем из union:
type ActiveStatus = Exclude<Status, 'cancelled'>
// 'pending' | 'success' | 'error'
// Оставляем только совпадающие:
type FinalStatus = Extract<Status, 'success' | 'error'>
// 'success' | 'error'
// NonNullable — убирает null и undefined:
type Name = string | null | undefined
type DefiniteName = NonNullable<Name> // stringfunction createUser(name: string, age: number): User {
return { id: Date.now(), name, email: '', age }
}
type UserType = ReturnType<typeof createUser> // User
type CreateParams = Parameters<typeof createUser> // [string, number]
class UserService {
findById(id: number): User { /* ... */ }
}
type ServiceInstance = InstanceType<typeof UserService>
// UserService — тип экземпляра класса// Partial реализован так:
type MyPartial<T> = {
[K in keyof T]?: T[K]
// K перебирает все ключи T
// ? делает поле опциональным
// T[K] — тип значения для ключа K
}
// Pick реализован так:
type MyPick<T, K extends keyof T> = {
[P in K]: T[P]
// Перебираем только ключи из K
}
// Record реализован так:
type MyRecord<K extends keyof any, V> = {
[P in K]: V
}interface Config {
apiUrl: string
timeout: number
retries: number
debug: boolean
}
// Дефолтные значения:
const defaults: Config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3,
debug: false,
}
// Пользователь передаёт только то что хочет изменить:
function createConfig(overrides: Partial<Config> = {}): Config {
return { ...defaults, ...overrides }
}
const config = createConfig({ debug: true, timeout: 10000 })
// { apiUrl: 'https://...', timeout: 10000, retries: 3, debug: true }Реализация pick, omit, merge как функции. Практичный пример: данные из API + merge с дефолтами через Partial-паттерн
// В TypeScript: Pick<T, keys>, Omit<T, keys>, Partial<T>
// В JS — реализуем как функции
function pick(obj, keys) {
return keys.reduce((acc, key) => {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
acc[key] = obj[key]
}
return acc
}, {})
}
function omit(obj, keys) {
const keySet = new Set(keys)
return Object.fromEntries(
Object.entries(obj).filter(([k]) => !keySet.has(k))
)
}
// Merge: накладываем partial поверх base (только существующие ключи)
function mergePartial(base, partial) {
const result = { ...base }
for (const key of Object.keys(partial)) {
if (Object.prototype.hasOwnProperty.call(base, key)) {
result[key] = partial[key]
}
}
return result
}
// Merge: сливаем все ключи (расширяющий merge)
function merge(base, partial) {
return { ...base, ...partial }
}
// Readonly-заморозка объекта (как Readonly<T>)
function readonly(obj) {
return Object.freeze({ ...obj })
}
// required — убеждаемся что все поля непустые
function assertRequired(obj, requiredKeys) {
const missing = requiredKeys.filter(k => obj[k] == null)
if (missing.length > 0) {
throw new Error(`Отсутствуют обязательные поля: ${missing.join(', ')}`)
}
return obj
}
// --- Практичный пример: API данные + дефолты ---
const userDefaults = {
id: null,
name: 'Аноним',
email: '',
age: 0,
role: 'user',
active: true,
createdAt: new Date().toISOString(),
}
// Данные пришли из API — могут быть неполными
const apiResponse = {
id: 42,
name: 'Алексей Петров',
email: 'alex@mail.ru',
}
// Применяем паттерн Partial<User>: только переданные поля перезаписывают дефолты
const fullUser = merge(userDefaults, apiResponse)
console.log('=== Полный объект пользователя ===')
console.log(fullUser)
// Pick — берём только нужные поля для карточки профиля
const profileCard = pick(fullUser, ['id', 'name', 'email', 'role'])
console.log('\n=== Карточка профиля (Pick) ===')
console.log(profileCard)
// Omit — убираем служебные поля перед отправкой
const publicUser = omit(fullUser, ['createdAt', 'active'])
console.log('\n=== Публичные данные (Omit) ===')
console.log(publicUser)
// Readonly — замораживаем конфигурацию
const config = readonly({
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3,
})
console.log('\n=== Readonly конфигурация ===')
console.log(config)
try {
config.timeout = 10000 // В strict mode — TypeError, иначе тихо игнорируется
} catch (e) {
console.log(`Нельзя изменить readonly: ${e.message}`)
}
// assertRequired — валидация
console.log('\n=== Валидация required полей ===')
try {
assertRequired({ name: 'Алексей', email: null }, ['name', 'email', 'id'])
} catch (e) {
console.log(`Ошибка: ${e.message}`) // 'Отсутствуют обязательные поля: email, id'
}
const validated = assertRequired(
{ name: 'Алексей', email: 'a@b.ru', id: 1 },
['name', 'email', 'id']
)
console.log(`Валидация прошла: ${validated.name}`)Реализуй три функции: `pick(obj, keys)` — возвращает новый объект только с указанными ключами, `omit(obj, keys)` — возвращает новый объект без указанных ключей, `merge(base, partial)` — возвращает новый объект где поля из partial перезаписывают поля base (остальные поля base сохраняются). Исходные объекты не мутируются.
pick: keys.reduce((acc, k) => { if (k in obj) acc[k] = obj[k]; return acc }, {}). omit: создай Set из keys, затем Object.fromEntries(Object.entries(obj).filter(([k]) => !keySet.has(k))). merge: return { ...base, ...partial }.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке