7.1 Union Types (Типы объединений)
В TypeScript есть довольно-таки редкая вещь — Union и Intersection Types. Это возможность создавать новые типы в виде выражений из других типов. Эти «типы» позволяют создавать сложные и многогранные типовые структуры, обеспечивая высокую степень гибкости и контроля над типизацией.
Union Types позволяют переменной принимать значения нескольких различных типов. Это особенно полезно, когда функция или метод может работать с различными типами данных, и вы хотите явно указать все возможные типы.
Синтаксис
Union Types создаются с использованием символа вертикальной черты |, который указывает, что переменная может иметь значение одного из перечисленных типов.
let name: type1 | type2 | ...;
Пример:
let value: string | number;
value = 'Hello';
value = 42;
value = true; // Ошибка компиляции: Type 'boolean' is not assignable to type 'string | number'.
В этом примере переменная value может принимать значения типа string или number, но не типа boolean.
Применение Union Types в функциях
Union Types особенно полезны в функциях, которые могут принимать параметры нескольких типов.
Пример:
"use strict";
function printId(id) {
console.log(`ID: ${id}`);
}
printId('abc123'); // Вывод: ID: abc123
printId(123); // Вывод: ID: 123
function printId(id: string | number): void {
console.log(`ID: ${id}`);
}
printId('abc123'); // Вывод: ID: abc123
printId(123); // Вывод: ID: 123
В этом примере функция printId принимает параметр id, который может быть либо строкой, либо числом.
Сужение типов (Type Narrowing)
Когда вы работаете с Union Types, TypeScript автоматически выполняет сужение типов (Type Narrowing), чтобы определить конкретный тип переменной в определенном контексте.
Пример:
"use strict";
function formatValue(value) {
if (typeof value === 'string') {
return value.toUpperCase(); //TypeScript компилятор знает, что value тут это строка
}
else {
return value.toFixed(2); //TypeScript компилятор знает, что value тут это число
}
}
console.log(formatValue('hello')); // Вывод: HELLO
console.log(formatValue(123.456)); // Вывод: 123.46
function formatValue(value: string | number): string {
if (typeof value === 'string') {
return value.toUpperCase(); //TypeScript компилятор знает, что value тут это строка
} else {
return value.toFixed(2); //TypeScript компилятор знает, что value тут это число
}
}
console.log(formatValue('hello')); // Вывод: HELLO
console.log(formatValue(123.456)); // Вывод: 123.46
В этом примере TypeScript использует оператор typeof, чтобы определить, является ли value строкой или числом, и применяет соответствующие методы.
7.2 Intersection Types (Типы пересечений)
Intersection Types позволяют объединять несколько типов в один, требуя, чтобы переменная соответствовала всем указанным типам. Это полезно, когда вы хотите создать новый тип, который объединяет свойства нескольких других типов.
Синтаксис
Intersection Types создаются с использованием символа амперсанда "&", который указывает, что переменная должна удовлетворять всем перечисленным типам:
let name: type1 & type2 & ...;
Пример:
"use strict";
let staff = {
name: 'Alice',
age: 30,
employeeId: 12345,
position: 'Manager'
};
console.log(staff);
interface Person {
name: string;
age: number;
}
interface Employee {
employeeId: number;
position: string;
}
type StaffMember = Person & Employee;
let staff: StaffMember = {
name: 'Alice',
age: 30,
employeeId: 12345,
position: 'Manager'
};
console.log(staff);
В этом примере тип StaffMember является пересечением типов Person и Employee, и объект staff должен иметь свойства обоих этих типов.
Применение Intersection Types в функциях
Intersection Types могут использоваться в функциях, чтобы указать, что параметр должен соответствовать нескольким типам одновременно.
Пример:
"use strict";
function getEmployeeDetails(employee) {
return `${employee.name}, Age: ${employee.age}, ID: ${employee.employeeId}, Position: ${employee.position}`;
}
let employee = {
name: 'Bob',
age: 25,
employeeId: 67890,
position: 'Developer'
};
console.log(getEmployeeDetails(employee)); // Вывод: Bob, Age: 25, ID: 67890, Position: Developer
function getEmployeeDetails(employee: Person & Employee): string {
return `${employee.name}, Age: ${employee.age}, ID: ${employee.employeeId}, Position: ${employee.position}`;
}
let employee: Person & Employee = {
name: 'Bob',
age: 25,
employeeId: 67890,
position: 'Developer'
};
console.log(getEmployeeDetails(employee)); // Вывод: Bob, Age: 25, ID: 67890, Position: Developer
В этом примере функция getEmployeeDetails принимает параметр employee, который должен соответствовать как типу Person, так и типу Employee.
Преимущества использования Union и Intersection Types
- Гибкость: Union Types позволяют функции принимать аргументы различных типов, что делает их более универсальными.
- Сложные типы: Intersection Types позволяют создавать сложные типы, которые объединяют свойства нескольких других типов, что полезно для работы с объектами, которые должны соответствовать нескольким интерфейсам.
- Повышенная безопасность типов: Использование Union и Intersection Types помогает избежать ошибок типов, обеспечивая более строгую проверку на этапе компиляции.
7.3 Примеры комбинирования Union и Intersection Types
TypeScript позволяет комбинировать Union и Intersection Types для создания еще более сложных типовых структур.
Пример:
"use strict";
function getVehicle(vehicle) {
if (vehicle === 'car-plane') {
return {
drive() {
console.log('Driving');
},
fly() {
console.log('Flying');
}
};
}
else {
return null;
}
}
let carPlane = getVehicle('car-plane');
carPlane === null || carPlane === void 0 ? void 0 : carPlane.drive(); // Вывод: Driving
carPlane === null || carPlane === void 0 ? void 0 : carPlane.fly(); // Вывод: Flying
interface Drivable {
drive(): void;
}
interface Flyable {
fly(): void;
}
type Vehicle = Drivable & Flyable;
type VehicleType = 'car' | 'plane' | 'car-plane';
function getVehicle(vehicle: VehicleType): Vehicle | null {
if (vehicle === 'car-plane') {
return {
drive() {
console.log('Driving');
},
fly() {
console.log('Flying');
}
};
} else {
return null;
}
}
let carPlane = getVehicle('car-plane');
carPlane?.drive(); // Вывод: Driving
carPlane?.fly(); // Вывод: Flying
В этом примере Vehicle является пересечением типов Drivable и Flyable, а VehicleType является Union Type, представляющим возможные типы транспортных средств. Функция getVehicle возвращает объект, который может как ездить, так и летать, или null, если тип транспортного средства не совпадает.
Примеры использования Union и Intersection Types в реальных проектах
Union и Intersection Types часто используются в реальных проектах для обработки данных из различных источников, создания многофункциональных объектов и обеспечения гибкости при работе с API.
Пример:
"use strict";
function handleResponse(response) {
if (response.status === 200) {
console.log('Data:', response.data);
}
else {
console.error('Error:', response.error);
}
}
let successResponse = { status: 200, data: { id: 1, name: 'Test' } };
let errorResponse = { status: 404, error: 'Not Found' };
handleResponse(successResponse); // Вывод: Data: { id: 1, name: 'Test' }
handleResponse(errorResponse); // Вывод: Error: Not Found
type ApiResponse = { status: 200, data: any } | { status: number, error: string };
function handleResponse(response: ApiResponse): void {
if (response.status === 200) {
console.log('Data:', response.data);
} else {
console.error('Error:', response.error);
}
}
let successResponse: ApiResponse = { status: 200, data: { id: 1, name: 'Test' } };
let errorResponse: ApiResponse = { status: 404, error: 'Not Found' };
handleResponse(successResponse); // Вывод: Data: { id: 1, name: 'Test' }
handleResponse(errorResponse); // Вывод: Error: Not Found
В этом примере интерфейс ApiResponse использует Union Types для описания возможных структур ответа API. Функция handleResponse обрабатывает успешные и ошибочные ответы.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ