Type aliases — собственные имена типов
Научимся создавать собственные имена для типов и использовать union-типы для гибкой типизации переменных и функций
type, понять, что такое union-типы, и использовать их для написания чистого и понятного кода.
1. Что такое type alias — аналогия с ярлыком
Представьте, что у вас на рабочем столе лежит множество файлов. Каждый файл содержит важную информацию: один — с данными о клиенте, другой — о продукте, третий — о заказе. Каждый раз, когда вам нужно обратиться к какому-то типу данных, выненев указывать его полностью — перечислять все поля, все типы, все свойства. Это утомительно и неудобно.
Теперь представьте, что вы можете создать ярлык — короткое, понятное имя, которое ссылается на целый набор данных. Вместо того чтобы каждый раз описывать сложную структуру, вы просто используете этот ярлык. Вот именно это и делает type alias в TypeScript!
Type alias (псевдоним типа) — это возможность создать собственное имя для любого типа в TypeScript. Это как дать короткое и понятное имя сложной структуре данных. Вы создаёте имя один раз, а потом используете его сколько угодно раз в коде.
Без type alias вам пришлось бы каждый раз перечислять все свойства объекта. С type alias вы даёте структуре короткое имя и используете его везде. Это делает код чище, читабельнее и легче для сопровождения.
Type alias работает не только с объектами — вы можете давать имена любым типам: примитивам, массивам, функциям, union-типам и даже сложным вложенным структурам. Это один из самых универсальных инструментов TypeScript для организации типов.
2. Синтаксис: type Name = Type
Синтаксис type alias невероятно прост. Вы используете ключевое слово type, за которым следует имя (по конвенции — с большой буквы в PascalCase), знак равенства, а затем сам тип:
type ИмяТипа = Тип;
Вот базовый пример — создаём alias для простого string:
type UserName = string;
const name1: UserName = "Алексей";
const name2: UserName = "Мария";
В этом примере мы создали type alias с именем UserName, который является псевдонимом для типа string. Теперь вместо того чтобы каждый раз писать string, мы можем использовать более выразительное имя UserName.
type userName = string — TypeScript покажет ошибку, потому что имена начинающиеся с маленькой буквы зарезервированы для переменных и параметров функций.
Важно понимать: type alias — это только псевдоним. TypeScript не создаёт новый тип — он просто даёт уже существующему типу дополнительное имя. В результатах компиляции type alias полностью исчезает, заменяясь исходным типом. Это чисто «синтаксический сахар» для удобства разработчика.
Теперь давайте рассмотрим, какие типы можно обернуть в type alias, и разберём каждый случай подробно.
3. Type alias для примитивов
Вы можете создавать type alias для любого примитивного типа: string, number, boolean, null, undefined. Это особенно полезно, когда вы хотите подчеркнуть семантику переменной.
Возьмём типичную ситуацию: у вас есть переменные, которые хранят идентификаторы. Без type alias код выглядит так:
const userId: string = "user_123";
const orderId: string = "order_456";
const productId: string = "product_789";
const sessionId: string = "session_000";
Видите проблему? Все переменные имеют тип string, но это «сырой» тип, который ничего не говорит о назначении переменных. А теперь посмотрите, как улучшается код с type alias:
type UserId = string;
type OrderId = string;
type ProductId = string;
type SessionId = string;
const userId: UserId = "user_123";
const orderId: OrderId = "order_456";
const productId: ProductId = "product_789";
const sessionId: SessionId = "session_000";
Теперь, глядя на код, сразу понятно, какое назначение у каждой переменной. UserId, OrderId, ProductId — каждое имя рассказывает о своёмназначение.
Примеры для других примитивов:
type Age = number;
type IsAdmin = boolean;
type Temperature = number;
type Email = string;
type Rating = number;
type IsActive = boolean;
const userAge: Age = 25;
const adminFlag: IsAdmin = true;
const roomTemp: Temperature = 22.5;
const userEmail: Email = "user@example.com";
Обратите внимание:хотя Age, Temperature и Rating все являются number, TypeScript всё равно позволяет присваивать значения одного типа другому. Type alias — это просто имя, а не новый тип.
Age переменной типа Temperature, потому что оба они — number. Но в коде будет сразу видно, что вы делаете что-то странное.
Пример, где type alias для примитивов становится по-настоящему полезным:
type Celsius = number;
type Fahrenheit = number;
function celsiusToFahrenheit(temp: Celsius): Fahrenheit {
return (temp * 9 / 5) + 32;
}
function fahrenheitToCelsius(temp: Fahrenheit): Celsius {
return (temp - 32) * 5 / 9;
}
const boilingPoint: Celsius = 100;
const boilingInF = celsiusToFahrenheit(boilingPoint);
console.log(`Кипение: ${boilingPoint}°C = ${boilingInF}°F`);
Теперь, глядя на функцию celsiusToFahrenheit, сразу видно: принимает она температуру в Цельсиях и возвращает в Фаренгейтах. Это гораздо понятнее, чем если бы оба аргумента были number.
4. Type alias для объектов
Одно из самых частых применений type alias — создание имён для объектных типов. Здесь type alias раскрывается во всей красе, потому что позволяет избавиться от повторяющихся структур и сделать код значительно чище.
Представьте, что вы работаете над приложением для интернет-магазина и у вас есть множество мест, где используются данные о пользователе:
function createUser(name: string, email: string, age: number) {
return { name, email, age };
}
function sendWelcomeEmail(user: { name: string; email: string; age: number }) {
return `Привет, ${user.name}!`;
}
function validateUser(user: { name: string; email: string; age: number }) {
return user.name.length > 0;
}
Видите? Один и тот же объектный тип { name: string; email: string; age: number } повторяется дважды. Это нарушает принцип DRY (Don't Repeat Yourself). Давайте исправим это с помощью type alias:
type User = {
name: string;
email: string;
age: number;
};
function createUser(name: string, email: string, age: number): User {
return { name, email, age };
}
function sendWelcomeEmail(user: User): string {
return `Привет, ${user.name}!`;
}
function validateUser(user: User): boolean {
return user.name.length > 0;
}
Отлично! Теперь структура определена один раз, а используется везде. Если вам понадобится добавить новое свойство (например, phone), вы добавите его только в одно место — в определение User.
Теперь давайте рассмотрим более сложные объектные структуры:
type Product = {
id: number;
name: string;
price: number;
description: string;
inStock: boolean;
};
type Order = {
orderId: string;
customerName: string;
items: Product[];
totalAmount: number;
isPaid: boolean;
};
function calculateDiscount(order: Order): number {
if (order.items.length > 5) {
return order.totalAmount * 0.1;
}
return 0;
}
Здесь Order ссылается на Product в свойстве items. Это читается почти как обычное предложение: «Заказ содержит массив продуктов».
Также можно создавать type alias с опциональными свойствами (через ?):
type UserProfile = {
name: string;
email: string;
age?: number; // опциональное
phone?: string; // опциональное
address?: string; // опциональное
avatar?: string; // опциональное
};
const profile1: UserProfile = {
name: "Иван",
email: "ivan@mail.com"
};
const profile2: UserProfile = {
name: "Мария",
email: "maria@mail.com",
age: 28,
phone: "+7 999 123 4567",
avatar: "https://example.com/avatar.jpg"
};
Оба объекта соответствуют типу UserProfile, хотя у profile1 заполнены только обязательные поля.
5. Type alias для массивов
TypeScript поддерживает два способа создания типов для массивов, и оба можно использовать в type alias:
Способ 1: Синтаксис с квадратными скобками Type[]
Способ 2: Синтаксис с Array Array<Type>
Оба способа абсолютно эквивалентны, но большинство TypeScript-разработчиков предпочитают первый вариант за его краткость.
// Способ 1: синтаксис с квадратными скобками
type StringArray = string[];
type NumberArray = number[];
type BooleanArray = boolean[];
// Способ 2: синтаксис с Array
type StringArray2 = Array<string>;
type NumberArray2 = Array<number>;
// Использование
const names: StringArray = ["Аня", "Борис", "Вика"];
const scores: NumberArray = [95, 87, 92, 78];
const flags: BooleanArray = [true, false, true];
Теперь давайте создадим более практичные type alias для массивов:
type UserName = string;
type UserList = UserName[];
type Score = number;
type ScoreBoard = Score[];
const team: UserList = ["Алексей", "Борис", "Виктор"];
const results: ScoreBoard = [100, 85, 92, 78, 95];
А вот пример, где type alias для массива становится особенно полезным — когда вы работаете с массивами объектов:
type Todo = {
id: number;
title: string;
completed: boolean;
};
type TodoList = Todo[];
const myTodos: TodoList = [
{ id: 1, title: "Изучить TypeScript", completed: false },
{ id: 2, title: "Написать проект", completed: false },
{ id: 3, title: "Пройти собеседование", completed: false }
];
function getCompletedTodos(todos: TodoList): TodoList {
return todos.filter(todo => todo.completed);
}
function addTodo(todos: TodoList, title: string): TodoList {
const newId = Math.max(...todos.map(t => t.id)) + 1;
return [...todos, { id: newId, title, completed: false }];
}
string[]), его стоит использовать. Но если массив состоит из сложного объекта, лучше использовать inline-тип для лучшей читаемости.
6. Type alias для функций
TypeScript позволяет создавать type alias для функций. Это полезно, когда у вас есть несколько функций с одинаковой сигнатурой (одинаковыми параметрами и возвращаемым типом).
Синтаксис для type alias функции:
type ИмяФункции = (параметр1: Тип1, параметр2: Тип2) => ВозвращаемыйТип;
Практические примеры:
// Функция, которая принимает два числа и возвращает число
type MathOperation = (a: number, b: number) => number;
// Функция, которая принимает строку и возвращает boolean
type Validator = (input: string) => boolean;
// Функция без параметров, возвращает строку
type Greeting = () => string;
// Функция, которая ничего не возвращает (void)
type Logger = (message: string) => void;
А теперь давайте посмотрим, как использовать эти type alias в реальном коде:
type MathOperation = (a: number, b: number) => number;
// Все эти функции соответствуют типу MathOperation
const add: MathOperation = (a, b) => a + b;
const multiply: MathOperation = (a, b) => a * b;
const subtract: MathOperation = (a, b) => a - b;
const divide: MathOperation = (a, b) => a / b;
// Функция, принимающая MathOperation как параметр
function calculate(operation: MathOperation, x: number, y: number): number {
return operation(x, y);
}
console.log(calculate(add, 5, 3)); // 8
console.log(calculate(multiply, 5, 3)); // 15
console.log(calculate(divide, 10, 2)); // 5
Обратите внимание, как удобно: функция calculate принимает любую функцию, соответствующую типу MathOperation, и работает с ней. Это открывает возможности для написания гибкого и переиспользуемого кода.
Ещё один полезный пример — type alias для callback-функций:
type Callback = (error: Error | null, data?: any) => void;
type AsyncOperation = (url: string, callback: Callback) => void;
function fetchData(url: string, callback: Callback): void {
// имитация запроса
callback(null, { status: "ok" });
}
// Использование
fetchData("https://api.example.com/users", (error, data) => {
if (error) {
console.error(error.message);
return;
}
console.log("Данные получены:", data);
});
7. Union типы в type
Union-типы — это одна из самых мощных возможностей TypeScript. Union позволяет переменной принимать значение одного из нескольких типов. Вы создаёте union, используя символ вертикальной черты | (pipe) между типами.
Представьте, что у вас есть переменная, которая может быть либо строкой, либо числом:
type StringOrNumber = string | number;
let value1: StringOrNumber = "привет"; // работает
let value2: StringOrNumber = 42; // работает
let value3: StringOrNumber = true; // ОШИБКА! boolean не входит в union
В этом примере StringOrNumber — это union-тип, который принимает либо string, либо number. Попытка присвоить boolean вызовет ошибку компиляции.
Union типы очень полезны для функций, которые могут принимать разные типы аргументов:
type ID = string | number;
function findUser(id: ID): string {
if (typeof id === "string") {
return `Поиск по строковому ID: ${id}`;
}
return `Поиск по числовому ID: ${id.toFixed(0)}`;
}
console.log(findUser("user_123")); // Поиск по строковому ID: user_123
console.log(findUser(42)); // Поиск по числовому ID: 42
Внутри функции TypeScript «сужает» тип: после проверки typeof id === "string" он знает, что в первой ветке id — это строка, а во второй — число.
Вот ещё несколько практических примеров union-типов:
// Переменная может быть строкой, числом или null
type SearchResult = string | number | null;
// Функция может вернуть строку или ошибку
type ApiResponse = string | Error;
// Значение может быть одним из нескольких литералов
type HttpStatus = 200 | 301 | 404 | 500;
// Комбинация примитивов и объектов
type FlexibleValue = string | number | { value: string };
let result: FlexibleValue;
result = "текст";
result = 123;
result = { value: "объект" };
Одно из самых распространённых применений union-типов — обработка результатов, которые могут быть либо успешными, либо ошибочными:
type SuccessResult = {
success: true;
data: string;
};
type ErrorResult = {
success: false;
error: string;
};
type Result = SuccessResult | ErrorResult;
function processData(input: string): Result {
if (input.length === 0) {
return { success: false, error: "Пустой ввод" };
}
return { success: true, data: input.toUpperCase() };
}
const result = processData("привет");
if (result.success) {
console.log(result.data); // ПРИВЕТ
} else {
console.error(result.error); // не выполнится
}
result.data, не проверив сначала result.success === true. Это называется сужение типа (type narrowing).
8. Union с числовыми литералами
Одна из самых интересных возможностей union-типов — использование литеральных значений вместо общих типов. Вы можете создать тип, который принимает только определённые значения.
Представьте, что у вас есть переменная, которая может быть только одним из нескольких предопределённых значений:
// Статус заказа может быть только одним из этих значений
type OrderStatus = "pending" | "processing" | "shipped" | "delivered" | "cancelled";
let status1: OrderStatus = "pending"; // работает
let status2: OrderStatus = "shipped"; // работает
let status3: OrderStatus = "wrong_status"; // ОШИБКА!
Это гораздо безопаснее, чем просто string! Если вы попытаетесь присвоить недопустимое значение, TypeScript сразу покажет ошибку.
Примеры использования строковых литералов:
// Уровень пользователя
type UserLevel = "beginner" | "intermediate" | "advanced" | "expert";
// День недели
type DayOfWeek = "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday";
// Цвет интерфейса
type Theme = "light" | "dark" | "system";
// Формат файла
type FileFormat = "json" | "xml" | "csv" | "txt";
Числовые литералы работают аналогично:
// HTTP статус-коды
type HttpStatusCode = 200 | 201 | 301 | 400 | 401 | 403 | 404 | 500;
// Уровень логирования
type LogLevel = 1 | 2 | 3 | 4 | 5;
// Рейтинг
type StarRating = 1 | 2 | 3 | 4 | 5;
function rateProduct(rating: StarRating): string {
switch (rating) {
case 5: return "Отлично!";
case 4: return "Хорошо";
case 3: return "Нормально";
case 2: return "Плохо";
case 1: return "Ужасно";
}
}
rateProduct(5); // Отлично!
rateProduct(3); // Нормально
rateProduct(6); // ОШИБКА! 6 не входит в тип
type FlexibleRating = StarRating | "none" — добавляет значение "none" к существующему типу.
9. Комбинирование union и объектных типов
Одна из самых мощных возможностей TypeScript — комбинирование union-типов с объектными типами. Это позволяет создавать гибкие структуры данных, которые могут принимать разные формы в зависимости от контекста.
Рассмотрим реальный пример: обработку событий в интерфейсе:
type MouseEvent = {
type: "mouse";
x: number;
y: number;
button: number;
};
type KeyboardEvent = {
type: "keyboard";
key: string;
code: number;
altKey: boolean;
};
type TouchEvent = {
type: "touch";
x: number;
y: number;
touches: number;
};
type AppEvent = MouseEvent | KeyboardEvent | TouchEvent;
function handleEvent(event: AppEvent): void {
switch (event.type) {
case "mouse":
console.log(`Клик в точке (${event.x}, ${event.y}) кнопкой ${event.button}`);
break;
case "keyboard":
console.log(`Нажата клавиша: ${event.key}`);
break;
case "touch":
console.log(`Касание в точке (${event.x}, ${event.y}) ${event.touches} пальцев`);
break;
}
}
Здесь каждый объектный тип содержит поле type с литеральным значением. Когда вы проверяете event.type === "mouse", TypeScript автоматически «сужает» тип до MouseEvent, и вы получаете доступ к свойствам x, y и button.
Другой полезный пример — обработка результатов API-запросов:
type LoadingState = {
status: "loading";
};
type SuccessState<T> = {
status: "success";
data: T;
};
type ErrorState = {
status: "error";
message: string;
};
type AsyncState<T> = LoadingState | SuccessState<T> | ErrorState;
// Использование с типом User
type User = { name: string; email: string };
type UserState = AsyncState<User>;
function renderUserState(state: UserState): string {
switch (state.status) {
case "loading":
return "Загрузка...";
case "success":
return `Пользователь: ${state.data.name}`;
case "error":
return `Ошибка: ${state.message}`;
}
}
status) с уникальным литеральным значением. TypeScript использует это поле для автоматического сужения типа — вы можете использовать switch или if для определения конкретного типа.
10. Type alias vs interface
В TypeScript есть два способа определять пользовательские типы: type и interface. Многие новички задаются вопросом: в чём разница? Давайте разберёмся.
Синтаксис:
// Type alias
type UserType = {
name: string;
email: string;
};
// Interface
interface UserInterface {
name: string;
email: string;
}
Для объектов оба варианта работают одинаково. Но есть несколько ключевых различий:
1. Interface можно расширять (extends):
// Interface можно расширять
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
// Type alias можно использовать с intersection (&)
type AnimalType = {
name: string;
};
type DogType = AnimalType & { breed: string };
2. Type alias для union-типов:
// Type alias для union
type StringOrNumber = string | number;
// Type alias для примитивов
type ID = string | number;
// Interface НЕ поддерживает union
// interface StringOrNumber = string | number; // ОШИБКА!
3. Declaration merging (только interface):
// Interface можно объявлять несколько раз — они сливаются
interface Window {
title: string;
}
interface Window {
alert(s: string): void;
}
// Window теперь имеет и title, и alert
Вот сводная таблица различий:
| Возможность | type | interface |
|---|---|---|
| Объектные типы | Да | Да |
| Union-типы | Да | Нет |
| Primitives | Да | Нет |
| extends (наследование) | Нет | Да |
| Declaration merging | Нет | Да |
| Compute types | Да | Нет |
interface — он лучше подходит для расширения и слияния. Для union-типов, примитивов и более сложных вычисляемых типов используйте type. На практике разница невелика, и многие команды используют оба варианта вперемешку.
11. Использование type alias в функциях
Type alias становится особенно полезным, когда вы используете сложные типы в параметрах и возвращаемых значениях функций. Давайте рассмотрим несколько практических примеров.
Пример 1: Функция для форматирования данных пользователя:
type User = {
firstName: string;
lastName: string;
age: number;
email: string;
};
type FormattedUser = {
fullName: string;
contactInfo: string;
isAdult: boolean;
};
function formatUser(user: User): FormattedUser {
return {
fullName: `${user.firstName} ${user.lastName}`,
contactInfo: `${user.email} (возраст: ${user.age})`,
isAdult: user.age >= 18
};
}
const user: User = {
firstName: "Алексей",
lastName: "Иванов",
age: 25,
email: "alex@example.com"
};
const formatted = formatUser(user);
console.log(formatted.fullName); // "Алексей Иванов"
console.log(formatted.isAdult); // true
Пример 2: Функция с опциональными параметрами:
type PaginationOptions = {
page?: number;
limit?: number;
sortBy?: string;
order?: "asc" | "desc";
};
type PageResult<T> = {
items: T[];
total: number;
page: number;
hasMore: boolean;
};
function getItems<T>(
items: T[],
options: PaginationOptions = {}
): PageResult<T> {
const { page = 1, limit = 10 } = options;
const start = (page - 1) * limit;
const end = start + limit;
return {
items: items.slice(start, end),
total: items.length,
page,
hasMore: end < items.length
};
}
// Использование
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
const page1 = getItems(numbers, { page: 1, limit: 3 });
console.log(page1.items); // [1, 2, 3]
console.log(page1.hasMore); // true
Пример 3: Функция с union-типами в параметрах:
type StringOrNumber = string | number;
type Callback = (value: string) => void;
function processValue(input: StringOrNumber, callback: Callback): void {
let result: string;
if (typeof input === "string") {
result = input.toUpperCase();
} else {
result = input.toString();
}
callback(result);
}
processValue("привет", (value) => console.log(value)); // ПРИВЕТ
processValue(42, (value) => console.log(value)); // 42
function save(user: { name: string; email: string; age: number }) лучше написать function save(user: User).
12. Вложенные type aliases
Type alias может ссылаться на другие type alias, создавая сложные структуры данных. Это позволяет разбивать сложные типы на более мелкие и управляемые части.
Пример: определение структуры для интернет-магазина:
// Базовые типы
type Money = number;
type Currency = "RUB" | "USD" | "EUR";
type Email = string;
type PhoneNumber = string;
// Промежуточные типы
type Address = {
street: string;
city: string;
country: string;
zipCode: string;
};
type ContactInfo = {
email: Email;
phone: PhoneNumber;
address: Address;
};
// Составные типы
type Product = {
id: number;
name: string;
price: Money;
currency: Currency;
inStock: boolean;
};
type OrderItem = {
product: Product;
quantity: number;
subtotal: Money;
};
type Order = {
id: string;
items: OrderItem[];
total: Money;
currency: Currency;
shippingAddress: Address;
status: "pending" | "paid" | "shipped" | "delivered";
};
type Customer = {
id: number;
name: string;
contact: ContactInfo;
orders: Order[];
};
Обратите внимание, как каждый тип собирается из предыдущих. Это создаёт «пирамиду» типов, где каждый уровень добавляет новую абстракцию. Такой подход делает код чище и проще для понимания.
Ещё один пример — типизация конфигурации приложения:
type DatabaseConfig = {
host: string;
port: number;
username: string;
password: string;
database: string;
};
type ServerConfig = {
port: number;
host: string;
cors: boolean;
};
type LogConfig = {
level: "debug" | "info" | "warn" | "error";
file?: string;
};
type AppConfig = {
env: "development" | "production" | "test";
database: DatabaseConfig;
server: ServerConfig;
logging: LogConfig;
};
const config: AppConfig = {
env: "development",
database: {
host: "localhost",
port: 5432,
username: "admin",
password: "secret",
database: "myapp"
},
server: {
port: 3000,
host: "localhost",
cors: true
},
logging: {
level: "debug"
}
};
Вложенные type alias позволяют создавать сложные структуры, оставаясь при этом читабельными. Каждый тип решает одну задачу, а верхнеуровневый тип собирает их вместе.
13. Ошибки новичков
При работе с type alias новички часто допускают ошибки. Давайте рассмотрим самые распространённые из них, чтобы вы могли их избежать.
Ошибка 1: Забытая точка с запятой в объектном типе
// НЕПРАВИЛЬНО — пропущена точка с запятой
type User = {
name: string
email: string // ← ошибка!
}
// ПРАВИЛЬНО
type User = {
name: string; // ← точка с запятой
email: string; // ← точка с запятой
};
Ошибка 2: Использование маленькой буквы для имени типа
// НЕПРАВИЛЬНО — имя начинается с маленькой буквы
type user = { // ← ошибка!
name: string;
};
// ПРАВИЛЬНО — PascalCase
type User = {
name: string;
};
Ошибка 3: Путаница между type и interface
// type для union
type StringOrNumber = string | number; // OK
// interface для union — ОШИБКА!
// interface StringOrNumber = string | number; // НЕ РАБОТАЕТ
// interface для расширения
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
Ошибка 4: Попытка использовать тип после присваивания (runtime)
type User = {
name: string;
age: number;
};
// Попытка проверить тип в runtime — ОШИБКА
const user = { name: "Алексей", age: 25 };
// Неправильно — type существует только в compile-time
// if (user instanceof User) { ... } // НЕ РАБОТАЕТ
// Правильно — проверяем наличие свойств
if ("name" in user && "age" in user) {
console.log("Это User!");
}
Ошибка 5: Забытый return в функции, возвращающей type alias
type Point = { x: number; y: number };
// НЕПРАВИЛЬНО — забыли return
function createPoint(x: number, y: number): Point {
{ x, y } // ← ошибка! Нет return
}
// ПРАВИЛЬНО
function createPoint(x: number, y: number): Point {
return { x, y };
}
Ошибка 6: Лишний type alias для простых случаев
// Плохо — слишком простой type alias
type StringType = string;
type NumberType = number;
// Это бессмысленно — просто используйте string и number
// Хорошо — осмысленные имена
type UserName = string; // имеет смысл — это имя пользователя
type UserId = string; // имеет смысл — это идентификатор
string или number достаточно понятен в контексте — используйте его напрямую.
Практический пример: Полная система типов для приложения
Давайте соберём всё вместе и создадим полноценную систему типов для небольшого приложения — список задач (todo list):
// ==========================================
// Система типов для приложения "Список задач"
// ==========================================
// Базовые примитивные type alias
type TodoId = number;
type UserId = number;
type Title = string;
type Description = string;
type Priority = "low" | "medium" | "high";
type Status = "todo" | "in_progress" | "done";
// Объектные type alias
type Todo = {
id: TodoId;
title: Title;
description?: Description;
priority: Priority;
status: Status;
createdAt: Date;
completedAt?: Date;
};
type User = {
id: UserId;
name: string;
email: string;
};
// Type alias для массивов
type TodoList = Todo[];
type UserList = User[];
// Type alias для функций
type FilterFn = (todo: Todo) => boolean;
type SortFn = (a: Todo, b: Todo) => number;
type Callback = (todo: Todo) => void;
// Union type для фильтрации
type FilterStatus = Status | "all";
type FilterPriority = Priority | "all";
// Сложный union для результатов
type OperationResult = {
success: true;
todo: Todo;
} | {
success: false;
error: string;
};
// ==========================================
// Функции с использованием type alias
// ==========================================
function createTodo(
title: Title,
priority: Priority,
description?: Description
): Todo {
return {
id: Date.now(),
title,
description,
priority,
status: "todo",
createdAt: new Date()
};
}
function filterTodos(todos: TodoList, filter: FilterFn): TodoList {
return todos.filter(filter);
}
function getTodosByStatus(
todos: TodoList,
status: FilterStatus
): TodoList {
if (status === "all") {
return todos;
}
return todos.filter(todo => todo.status === status);
}
function getTodosByPriority(
todos: TodoList,
priority: FilterPriority
): TodoList {
if (priority === "all") {
return todos;
}
return todos.filter(todo => todo.priority === priority);
}
function completeTodo(todo: Todo): Todo {
return {
...todo,
status: "done" as Status,
completedAt: new Date()
};
}
// ==========================================
// Использование
// ==========================================
const todos: TodoList = [
createTodo("Изучить TypeScript", "high"),
createTodo("Написать проект", "medium"),
createTodo("Выпить кофе", "low"),
];
const highPriority = getTodosByPriority(todos, "high");
console.log(`Высокий приоритет: ${highPriority.length} задач`);
const allTodos = getTodosByStatus(todos, "all");
console.log(`Всего задач: ${allTodos.length}`);
Этот пример демонстрирует, как type alias могут организовать типизацию целого приложения. Каждый тип имеет осмысленное имя, функции используют эти типы, а код остаётся чистым и читабельным.
Итоги урока
Поздравляем! Вы изучили один из фундаментальных инструментов TypeScript. Давайте подведём итоги:
Что мы изучили:
- Что такое type alias — собственные имена для типов, которые делают код чище и понятнее
- Синтаксис —
type Name = Type;с именами в PascalCase - Примитивные type alias — дают осмысленные имена примитивным типам для улучшения читаемости
- Объектные type alias — определяют структуру объектов и избавляются от дублирования
- Массивные type alias —
Type[]иArray<Type> - Функциональные type alias — определяют сигнатуру функций
- Union-типы — переменная может быть одним из нескольких типов (через
|) - Литеральные union-типы — ограничение значений конкретными литералами
- Комбинирование — union + объектные типы для гибких структур данных
- type vs interface — когда что использовать
- Вложенные type alias — создание сложных структур из простых частей
- Типичные ошибки — как их избежать
Советы для запоминания:
- Начните с простого — создавайте type alias для примитивов и смотрите, как код становится чище
- Используйте union-типы для переменных, которые могут принимать разные значения
- Не злоупотребляйте — создавайте type alias только тогда, когда это добавляет ценность
- Практикуйтесь — создавайте type alias для реальных задач, с которыми вы сталкиваетесь
Это был последний урок Модуля 3!
Вы успешно освоили основы типов в TypeScript
Готовы? Переходите к Модулю 4: ФункцииПроверяем понимание
8 вопросов