Partial<T> создаёт тип, где все свойства T становятся необязательными. Идеально для функций обновления:
interface User {
id: number
name: string
email: string
age: number
}
// Partial<User> = { id?: number; name?: string; email?: string; age?: number }
function updateUser(user: User, changes: Partial<User>): User {
return { ...user, ...changes }
}
updateUser(user, { name: 'Новое имя' }) // OK — только name
updateUser(user, { age: 30, email: 'x@y.z' }) // OK — несколько полей
updateUser(user, {}) // OK — ничего не меняемRequired<T> — обратное Partial. Убирает опциональность у всех свойств:
interface Config {
host?: string
port?: number
debug?: boolean
}
type StrictConfig = Required<Config>
// { host: string; port: number; debug: boolean } — всё обязательно
function initApp(config: Required<Config>) { /* ... */ }Pick<T, K> создаёт тип только с указанными ключами:
interface User {
id: number
name: string
email: string
password: string
createdAt: Date
}
// Публичные данные (без password):
type PublicUser = Pick<User, 'id' | 'name' | 'email'>
// { id: number; name: string; email: string }
// Только для отображения в списке:
type UserListItem = Pick<User, 'id' | 'name'>
// { id: number; name: string }Omit<T, K> — обратное Pick. Создаёт тип без указанных ключей:
// Пользователь для создания (без id и createdAt — они генерируются):
type CreateUserDto = Omit<User, 'id' | 'createdAt'>
// { name: string; email: string; password: string }
// Без чувствительных данных:
type SafeUser = Omit<User, 'password'>
// { id: number; name: string; email: string; createdAt: Date }Record<K, V> создаёт тип объекта с ключами типа K и значениями типа V:
type HttpStatus = 200 | 400 | 404 | 500
const statusMessages: Record<HttpStatus, string> = {
200: 'OK',
400: 'Bad Request',
404: 'Not Found',
500: 'Internal Server Error',
}
// Словарь пользователей по id:
type UserCache = Record<number, User>
const cache: UserCache = {}
cache[1] = { id: 1, name: 'Алексей', ... }
// Счётчики по строковым ключам:
type Counters = Record<string, number>
const counters: Counters = {}
counters['clicks'] = 5Пересечение типов (&) объединяет несколько типов в один:
interface WithId {
id: number
}
interface WithTimestamps {
createdAt: Date
updatedAt: Date
}
interface WithSoftDelete {
deletedAt: Date | null
}
// Модель базы данных со всеми полями:
type DbModel = WithId & WithTimestamps & WithSoftDelete & {
// специфичные поля
}
// Функция принимает любой объект с id:
function findById<T extends WithId>(items: T[], id: number): T | undefined {
return items.find(item => item.id === id)
}Утилитные типы можно комбинировать:
// Форма создания: без id, все поля обязательны
type CreateDto<T> = Required<Omit<T, 'id'>>
// Форма обновления: без id, все поля опциональны
type UpdateDto<T> = Partial<Omit<T, 'id'>>
type CreateUser = CreateDto<User> // { name: string; email: string; ... }
type UpdateUser = UpdateDto<User> // { name?: string; email?: string; ... }Паттерны Partial, Required, Pick, Omit, Record через JavaScript функции
// В TS: Partial<T>, Pick<T, K>, Omit<T, K>, Record<K, V> — утилитные типы
// В JS: реализуем их как runtime-функции
// === Partial: обновление объекта ===
function updateObject(original, changes) {
// В TS: function update<T>(obj: T, changes: Partial<T>): T
return { ...original, ...changes }
}
const user = { id: 1, name: 'Алексей', email: 'alex@test.com', age: 28 }
console.log('=== Partial (updateObject) ===')
console.log(updateObject(user, { name: 'Дмитрий' }))
// { id: 1, name: 'Дмитрий', email: 'alex@test.com', age: 28 }
console.log(updateObject(user, { age: 30, email: 'new@test.com' }))
// { id: 1, name: 'Алексей', email: 'new@test.com', age: 30 }
// === Pick: выбрать только нужные поля ===
function pick(obj, keys) {
// В TS: function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>
return keys.reduce((result, key) => {
if (key in obj) result[key] = obj[key]
return result
}, {})
}
console.log('\n=== Pick ===')
const publicUser = pick(user, ['id', 'name'])
console.log(publicUser) // { id: 1, name: 'Алексей' }
const userPreview = pick(user, ['id', 'name', 'email'])
console.log(userPreview) // { id: 1, name: 'Алексей', email: 'alex@test.com' }
// === Omit: исключить ненужные поля ===
function omit(obj, keys) {
// В TS: function omit<T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>
return Object.fromEntries(
Object.entries(obj).filter(([key]) => !keys.includes(key))
)
}
console.log('\n=== Omit ===')
const userWithoutAge = omit(user, ['age'])
console.log(userWithoutAge) // { id: 1, name: 'Алексей', email: 'alex@test.com' }
// Убрать id для DTO создания:
const createUserDto = omit(user, ['id'])
console.log(createUserDto) // { name: 'Алексей', email: 'alex@test.com', age: 28 }
// === Record: словарь с типизированными ключами ===
console.log('\n=== Record ===')
const statusMessages = {
200: 'OK',
201: 'Created',
400: 'Bad Request',
401: 'Unauthorized',
404: 'Not Found',
500: 'Internal Server Error',
}
// В TS: Record<200|201|400|401|404|500, string>
function getStatusMessage(code) {
return statusMessages[code] ?? `Неизвестный код: ${code}`
}
[200, 404, 500, 418].forEach(code => {
console.log(`${code}: ${getStatusMessage(code)}`)
})
// === Intersection (мixin паттерн) ===
console.log('\n=== Intersection (mixin) ===')
function withTimestamps(obj) {
// В TS: T & { createdAt: Date; updatedAt: Date }
return { ...obj, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }
}
function withSoftDelete(obj) {
// В TS: T & { deletedAt: Date | null }
return { ...obj, deletedAt: null }
}
const dbRecord = withSoftDelete(withTimestamps({ id: 1, name: 'Тест' }))
console.log(Object.keys(dbRecord)) // ['id', 'name', 'createdAt', 'updatedAt', 'deletedAt']Partial<T> создаёт тип, где все свойства T становятся необязательными. Идеально для функций обновления:
interface User {
id: number
name: string
email: string
age: number
}
// Partial<User> = { id?: number; name?: string; email?: string; age?: number }
function updateUser(user: User, changes: Partial<User>): User {
return { ...user, ...changes }
}
updateUser(user, { name: 'Новое имя' }) // OK — только name
updateUser(user, { age: 30, email: 'x@y.z' }) // OK — несколько полей
updateUser(user, {}) // OK — ничего не меняемRequired<T> — обратное Partial. Убирает опциональность у всех свойств:
interface Config {
host?: string
port?: number
debug?: boolean
}
type StrictConfig = Required<Config>
// { host: string; port: number; debug: boolean } — всё обязательно
function initApp(config: Required<Config>) { /* ... */ }Pick<T, K> создаёт тип только с указанными ключами:
interface User {
id: number
name: string
email: string
password: string
createdAt: Date
}
// Публичные данные (без password):
type PublicUser = Pick<User, 'id' | 'name' | 'email'>
// { id: number; name: string; email: string }
// Только для отображения в списке:
type UserListItem = Pick<User, 'id' | 'name'>
// { id: number; name: string }Omit<T, K> — обратное Pick. Создаёт тип без указанных ключей:
// Пользователь для создания (без id и createdAt — они генерируются):
type CreateUserDto = Omit<User, 'id' | 'createdAt'>
// { name: string; email: string; password: string }
// Без чувствительных данных:
type SafeUser = Omit<User, 'password'>
// { id: number; name: string; email: string; createdAt: Date }Record<K, V> создаёт тип объекта с ключами типа K и значениями типа V:
type HttpStatus = 200 | 400 | 404 | 500
const statusMessages: Record<HttpStatus, string> = {
200: 'OK',
400: 'Bad Request',
404: 'Not Found',
500: 'Internal Server Error',
}
// Словарь пользователей по id:
type UserCache = Record<number, User>
const cache: UserCache = {}
cache[1] = { id: 1, name: 'Алексей', ... }
// Счётчики по строковым ключам:
type Counters = Record<string, number>
const counters: Counters = {}
counters['clicks'] = 5Пересечение типов (&) объединяет несколько типов в один:
interface WithId {
id: number
}
interface WithTimestamps {
createdAt: Date
updatedAt: Date
}
interface WithSoftDelete {
deletedAt: Date | null
}
// Модель базы данных со всеми полями:
type DbModel = WithId & WithTimestamps & WithSoftDelete & {
// специфичные поля
}
// Функция принимает любой объект с id:
function findById<T extends WithId>(items: T[], id: number): T | undefined {
return items.find(item => item.id === id)
}Утилитные типы можно комбинировать:
// Форма создания: без id, все поля обязательны
type CreateDto<T> = Required<Omit<T, 'id'>>
// Форма обновления: без id, все поля опциональны
type UpdateDto<T> = Partial<Omit<T, 'id'>>
type CreateUser = CreateDto<User> // { name: string; email: string; ... }
type UpdateUser = UpdateDto<User> // { name?: string; email?: string; ... }Паттерны Partial, Required, Pick, Omit, Record через JavaScript функции
// В TS: Partial<T>, Pick<T, K>, Omit<T, K>, Record<K, V> — утилитные типы
// В JS: реализуем их как runtime-функции
// === Partial: обновление объекта ===
function updateObject(original, changes) {
// В TS: function update<T>(obj: T, changes: Partial<T>): T
return { ...original, ...changes }
}
const user = { id: 1, name: 'Алексей', email: 'alex@test.com', age: 28 }
console.log('=== Partial (updateObject) ===')
console.log(updateObject(user, { name: 'Дмитрий' }))
// { id: 1, name: 'Дмитрий', email: 'alex@test.com', age: 28 }
console.log(updateObject(user, { age: 30, email: 'new@test.com' }))
// { id: 1, name: 'Алексей', email: 'new@test.com', age: 30 }
// === Pick: выбрать только нужные поля ===
function pick(obj, keys) {
// В TS: function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>
return keys.reduce((result, key) => {
if (key in obj) result[key] = obj[key]
return result
}, {})
}
console.log('\n=== Pick ===')
const publicUser = pick(user, ['id', 'name'])
console.log(publicUser) // { id: 1, name: 'Алексей' }
const userPreview = pick(user, ['id', 'name', 'email'])
console.log(userPreview) // { id: 1, name: 'Алексей', email: 'alex@test.com' }
// === Omit: исключить ненужные поля ===
function omit(obj, keys) {
// В TS: function omit<T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>
return Object.fromEntries(
Object.entries(obj).filter(([key]) => !keys.includes(key))
)
}
console.log('\n=== Omit ===')
const userWithoutAge = omit(user, ['age'])
console.log(userWithoutAge) // { id: 1, name: 'Алексей', email: 'alex@test.com' }
// Убрать id для DTO создания:
const createUserDto = omit(user, ['id'])
console.log(createUserDto) // { name: 'Алексей', email: 'alex@test.com', age: 28 }
// === Record: словарь с типизированными ключами ===
console.log('\n=== Record ===')
const statusMessages = {
200: 'OK',
201: 'Created',
400: 'Bad Request',
401: 'Unauthorized',
404: 'Not Found',
500: 'Internal Server Error',
}
// В TS: Record<200|201|400|401|404|500, string>
function getStatusMessage(code) {
return statusMessages[code] ?? `Неизвестный код: ${code}`
}
[200, 404, 500, 418].forEach(code => {
console.log(`${code}: ${getStatusMessage(code)}`)
})
// === Intersection (мixin паттерн) ===
console.log('\n=== Intersection (mixin) ===')
function withTimestamps(obj) {
// В TS: T & { createdAt: Date; updatedAt: Date }
return { ...obj, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }
}
function withSoftDelete(obj) {
// В TS: T & { deletedAt: Date | null }
return { ...obj, deletedAt: null }
}
const dbRecord = withSoftDelete(withTimestamps({ id: 1, name: 'Тест' }))
console.log(Object.keys(dbRecord)) // ['id', 'name', 'createdAt', 'updatedAt', 'deletedAt']Реализуй функцию `createRepository(items)` которая возвращает объект с методами: findById(id) — найти по id, findAll() — все элементы, create(data) — создать с автоинкрементным id, update(id, changes) — обновить часть полей (как Partial), remove(id) — удалить и вернуть удалённый элемент или null.
findById: items.find(item => item.id === id). findAll: [...items]. create: const newItem = { id: nextId++, ...data }; items.push(newItem); return newItem. update: найди через findIndex, если -1 верни null, иначе items[idx] = { ...items[idx], ...changes }, верни items[idx]. remove: найди через findIndex, используй items.splice(idx, 1)[0].
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке