6.1 Partial
Утилитарные типы (Utility Types) в TypeScript — это шикарные инструменты, которые позволяют создавать новые типы на основе существующих. Эти типы упрощают работу с объектами, делая код более гибким и повторно используемым. В этой лекции мы рассмотрим основные утилитарные типы в TypeScript: Partial, Readonly, Record, Pick и Omit, их синтаксис и примеры использования.
Тип Partial делает все свойства типа Type необязательными. Это полезно, когда нужно создать объект, который может содержать лишь часть свойств другого типа.
Синтаксис:
type Partial<Type> = {
[P in keyof Type]?: Type[P];
};
Пример использования:
"use strict";
// Пример массива пользователей
let users = [
{ id: 1, name: "John", email: "john@example.com" },
{ id: 2, name: "Bob", email: "bob@example.com" },
];
// Функция обновления пользователя
function updateUser(id, userUpdates) {
const userIndex = users.findIndex(user => user.id === id);
if (userIndex !== -1) {
// Обновляем свойства пользователя
users[userIndex] = { ...users[userIndex], ...userUpdates };
console.log(`User with id ${id} updated:`, users[userIndex]);
}
else {
console.log(`User with id ${id} not found.`);
}
}
// Примеры вызова функции
updateUser(1, { name: "John Doe" });
updateUser(2, { email: "bob.williams@example.com" });
interface User {
id: number;
name: string;
email: string;
}
// Пример массива пользователей
let users: User[] = [
{ id: 1, name: "John", email: "john@example.com" },
{ id: 2, name: "Bob", email: "bob@example.com" },
];
// Функция обновления пользователя
function updateUser(id: number, userUpdates: Partial<User>): void {
const userIndex = users.findIndex(user => user.id === id);
if (userIndex !== -1) {
// Обновляем свойства пользователя
users[userIndex] = { ...users[userIndex], ...userUpdates };
console.log(`User with id ${id} updated:`, users[userIndex]);
} else {
console.log(`User with id ${id} not found.`);
}
}
// Примеры вызова функции
updateUser(1, { name: "John Doe"});
updateUser(2, { email: "bob.williams@example.com" });
В этом примере функция updateUser принимает объект userUpdates, который может содержать любую комбинацию свойств из интерфейса User.
6.2 Readonly
Тип Readonly делает все свойства типа Type только для чтения. Это полезно для создания неизменяемых объектов.
Синтаксис:
type Readonly<Type> = {
readonly [P in keyof Type]: Type[P];
};
Пример использования:
interface Point {
x: number;
y: number;
}
const point: Readonly<Point> = { x: 10, y: 20 };
// point.x = 5; // Ошибка: нельзя изменять свойство "x", так как оно доступно только для чтения
В этом примере объект point неизменяемый, и любые попытки изменить его свойства приведут к ошибке компиляции.
6.3 Record
Тип Record создает тип объекта с набором свойств K, каждый из которых имеет тип Type. Это полезно для создания словарей или карт с известным набором ключей.
Синтаксис:
type Record<K extends keyof any, Type> = {
[P in K]: Type;
};
Пример использования:
"use strict";
const rolePermissions = {
admin: { read: true, write: true, delete: true },
user: { read: true, write: false, delete: false },
guest: { read: true, write: false, delete: false }
};
console.log(rolePermissions.admin.read); // Вывод: true
type Role = "admin" | "user" | "guest";
interface Permissions {
read: boolean;
write: boolean;
delete: boolean;
}
const rolePermissions: Record<Role, Permissions> = {
admin: { read: true, write: true, delete: true },
user: { read: true, write: false, delete: false },
guest: { read: true, write: false, delete: false }
};
console.log(rolePermissions.admin.read); // Вывод: true
Здесь rolePermissions является объектом, где ключи соответствуют типу Role, а значения имеют тип Permissions.
6.4 Pick
Тип Pick создает новый тип, выбирая набор свойств K из типа Type. Это полезно, когда нужно создать тип с подмножеством свойств другого типа.
Синтаксис:
type Pick <Type, K extends keyof Type> = {
[P in K]: Type[P];
};
Пример использования:
"use strict";
const user = {
id: 1,
name: "Alice"
// email и password отсутствуют, так как они не выбраны
};
console.log(user); // Вывод: { id: 1, name: 'Alice' }
interface User {
id: number;
name: string;
email: string;
password: string;
}
type UserPreview = Pick<User, "id" | "name">
const user: UserPreview = {
id: 1,
name: "Alice"
// email и password отсутствуют, так как они не выбраны
};
console.log(user); // Вывод: { id: 1, name: 'Alice' }
Здесь тип UserPreview содержит только свойства id и name из интерфейса User.
6.5 Omit
Тип Omit создает новый тип, исключая набор свойств K из типа T. Это полезно, когда нужно создать тип без некоторых свойств другого типа.
Синтаксис 1:
type Omit<Type, K extends keyof any> = {
[P in K]: Type[P];
}
Синтаксис 2:
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Пример использования:
"use strict";
const user = {
id: 1,
name: "Alice",
email: "alice@example.com"
// password отсутствует, так как оно исключено
};
console.log(user); // Вывод: { id: 1, name: 'Alice', email: 'alice@example.com' }
interface User {
id: number;
name: string;
email: string;
password: string;
}
type UserWithoutPassword = Omit<User, "password">
const user: UserWithoutPassword = {
id: 1,
name: "Alice",
email: "alice@example.com"
// password отсутствует, так как оно исключено
};
console.log(user); // Вывод: { id: 1, name: 'Alice', email: 'alice@example.com' }
Здесь тип UserWithoutPassword содержит все свойства из интерфейса User, кроме свойства password.
6.6 Утилитарные типы с keyof и индексированием
TypeScript предоставляет несколько утилитарных типов, которые используют keyof и типы индексирования для создания новых типов на основе существующих.
Пример использования Partial
Тип Partial делает все свойства типа T необязательными. Он использует оператор keyof и типы индексирования.
"use strict";
let partialUser = {
name: "Dana"
// Свойства age и address необязательны
};
console.log(partialUser); // { name: 'Dana' }
interface Person {
name: string;
age: number;
address: string;
}
type Partial<T> = {
[P in keyof T]?: T[P];
};
let partialUser: Partial<Person> = {
name: "Dana"
// Свойства age и address необязательны
};
console.log(partialUser); // { name: 'Dana' }
Пример использования Pick
Тип Pick создает новый тип, выбирая набор свойств K из типа T. Он использует оператор keyof и типы индексирования для выбора свойств:
"use strict";
let personNameAge = {
name: "Eve",
age: 32
// Свойство address отсутствует, так как оно не выбрано
};
console.log(personNameAge); // { name: 'Eve', age: 32 }
interface Person {
name: string;
age: number;
address: string;
}
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type PersonNameAndAge = Pick<Person, "name" | "age">
let personNameAge: PersonNameAndAge = {
name: "Eve",
age: 32
// Свойство address отсутствует, так как оно не выбрано
};
console.log(personNameAge); // { name: 'Eve', age: 32 }
6.7 Реальные примеры
Мапированные типы широко используются в реальных проектах для улучшения типизации и упрощения работы с объектами.
Пример 1: использование типа Partial для создания типа валидации формы:
"use strict";
const errors = {
name: "Name is required",
email: "Invalid email address"
};
console.log(errors); // { name: 'Name is required', email: 'Invalid email address' }
interface FormValues {
name: string;
email: string;
password: string;
}
type FormErrors = Partial<Record<keyof FormValues, string>>;
const errors: FormErrors = {
name: "Name is required",
email: "Invalid email address"
};
console.log(errors); // { name: 'Name is required', email: 'Invalid email address' }
Пример 2: использование типа Readonly для создания неизменяемой конфигурации приложения:
interface Config {
apiUrl: string;
timeout: number;
}
const config: Readonly<Config> = {
apiUrl: "https://api.example.com",
timeout: 5000
};
// config.apiUrl = "https://newapi.example.com"; // Ошибка: нельзя изменять свойство "apiUrl"
Пример 3: использование типа Pick для создания типа, описывающего обновляемые свойства объекта:
interface User {
id: number;
name: string;
email: string;
password: string;
}
type UserUpdate = Pick<User, "name" | "email">;
function updateUser(id: number, updates: UserUpdate): void {
// Логика обновления пользователя
}
updateUser(1, { name: "Alice", email: "alice@example.com" });
Преимущества использования утилитарных типов
- Повышенная гибкость: утилитарные типы позволяют легко создавать новые типы на основе существующих, обеспечивая гибкость и повторное использование кода.
- Улучшенная типизация: утилитарные типы помогают улучшить типизацию, делая код более типобезопасным и сокращая вероятность ошибок.
- Упрощение работы с объектами: утилитарные типы упрощают работу с объектами, позволяя легко добавлять, удалять или изменять свойства объектов.
- Повышенная читаемость и поддерживаемость: утилитарные типы делают код более читаемым и легким для поддержки, так как они четко определяют структуры данных и их изменения.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ