Type Alias
Type alias — именованный тип, создаваемый ключевым словом
type. Позволяет дать имя любой комбинации типов.
Зачем нужно
- Сокращение длинных и сложных типов
- Переиспользование составных типов
- Семантическое именование:
type UserId = numberпонятнее чем простоnumber - Создание обобщённых (generic) типов
- Рекурсивные типы для древовидных структур
Где используется
- Везде, где нужно дать имя типу: параметры, возвращаемые значения, свойства
- Union и intersection типы
- Утилитарные и вспомогательные типы
- Conditional и mapped types (только через
type, неinterface)
Предпосылки
- Примитивные типы — базовые типы
- Union и Intersection — комбинирование типов
Базовый синтаксис
// type ИмяТипа = определение;
type ID = number;
type Name = string;
type IsActive = boolean;
// Использование
let userId: ID = 42;
let userName: Name = "Alice";
let active: IsActive = true;
Type alias для составных типов
// Union
type StringOrNumber = string | number;
type Nullable<T> = T | null;
type Optional<T> = T | undefined;
// Объект
type User = {
id: number;
name: string;
email: string;
};
// Функция
type Callback = (error: Error | null, data: string) => void;
type AsyncFn<T> = () => Promise<T>;
// Кортеж
type Point = [number, number];
type Entry = [string, unknown];
// Массив
type NumberList = number;
type UserList = User;
Type alias для объектов
type User = {
id: number;
name: string;
email: string;
age?: number; // Опциональное свойство
readonly createdAt: Date; // Только для чтения
};
// Index signature
type Dictionary = {
[key: string]: unknown;
};
type NumberMap = {
[key: string]: number;
};
// Computed properties
type Handlers = {
[K in "click" | "hover" | "focus"]: => void;
};
// { click: => void; hover: => void; focus: => void }
Generic Type Aliases
// Обобщённый тип с параметром
type Container<T> = {
value: T;
timestamp: Date;
};
const numContainer: Container<number> = { value: 42, timestamp: new Date };
const strContainer: Container<string> = { value: "hello", timestamp: new Date };
// Несколько параметров
type Pair<A, B> = {
first: A;
second: B;
};
const pair: Pair<string, number> = { first: "age", second: 30 };
// Параметр по умолчанию
type ApiResponse<T = unknown> = {
data: T;
status: number;
message: string;
};
const response: ApiResponse = { data: null, status: 200, message: "OK" };
const typedResponse: ApiResponse<User> = {
data: [{ id: 1, name: "Alice", email: "a@b.com" }],
status: 200,
message: "OK",
};
// Ограничение параметра (constraint)
type Identifiable<T extends { id: number }> = T & {
toString: string;
};
// Условный generic
type Flatten<T> = T extends Array<infer U> ? U : T;
type A = Flatten<string>; // string
type B = Flatten<number>; // number
Рекурсивные типы
// JSON-подобная структура
type Json =
| string
| number
| boolean
| null
| Json
| { [key: string]: Json };
const data: Json = {
name: "Alice",
age: 30,
scores: [95, 87, 92],
address: {
city: "Moscow",
coords: [55.75, 37.62],
},
};
// Древовидная структура
type TreeNode<T> = {
value: T;
children: TreeNode<T>;
};
const tree: TreeNode<string> = {
value: "root",
children: [
{
value: "child1",
children: ,
},
{
value: "child2",
children: [
{ value: "grandchild", children: },
],
},
],
};
// Связный список
type LinkedList<T> = {
value: T;
next: LinkedList<T> | null;
};
// Глубоко вложенный Partial
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
interface Config {
server: {
host: string;
port: number;
ssl: {
enabled: boolean;
cert: string;
};
};
}
// Все поля опциональные на любой глубине
const partial: DeepPartial<Config> = {
server: {
port: 3000,
},
};
Паттерны использования
Брендированные типы
type Brand<T, B extends string> = T & { readonly __brand: B };
type USD = Brand<number, "USD">;
type EUR = Brand<number, "EUR">;
type Meters = Brand<number, "Meters">;
type Seconds = Brand<number, "Seconds">;
function createUSD(amount: number): USD {
return amount as USD;
}
function addMoney(a: USD, b: USD): USD {
return (a + b) as USD;
}
const price = createUSD(100);
const tax = createUSD(10);
addMoney(price, tax); // OK
// addMoney(price, 42); // Ошибка! number !== USD
Утилитарные type aliases
// Nullable
type Nullable<T> = T | null | undefined;
// Промисифицированная версия типа
type Promisify<T> = {
[K in keyof T]: T[K] extends (...args: infer A) => infer R
? (...args: A) => Promise<R>
: T[K];
};
// Извлечение типа элемента массива
type ElementOf<T extends readonly unknown> = T[number];
const fruits = ["apple", "banana", "cherry"] as const;
type Fruit = ElementOf<typeof fruits>; // "apple" | "banana" | "cherry"
// Глубоко readonly
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};
Type alias для функций
// Типы функций
type Predicate<T> = (item: T) => boolean;
type Mapper<T, U> = (item: T) => U;
type Reducer<T, U> = (acc: U, item: T) => U;
type Comparator<T> = (a: T, b: T) => number;
// Использование
function filter<T>(arr: T, predicate: Predicate<T>): T {
return arr.filter(predicate);
}
const isEven: Predicate<number> = (n) => n % 2 === 0;
const evens = filter([1, 2, 3, 4, 5], isEven); // [2, 4]
// Event handler типы
type EventHandler<E = Event> = (event: E) => void;
type ClickHandler = EventHandler<MouseEvent>;
type KeyHandler = EventHandler<KeyboardEvent>;
Частые ошибки
- Путать type alias и новый тип — type alias это просто имя, не новый тип
type UserId = number;
type OrderId = number;
let userId: UserId = 1;
let orderId: OrderId = userId; // OK! Оба — number, нет защиты
// Для защиты — используйте брендированные типы
- Рекурсивный тип без базового случая — приведёт к бесконечной рекурсии
- Слишком сложные вложенные generics — ухудшают читаемость и производительность компилятора
- Не использовать generic где нужно — дублирование типов вместо параметризации
// Плохо: дублирование
type StringResponse = { data: string; status: number };
type NumberResponse = { data: number; status: number };
type UserResponse = { data: User; status: number };
// Хорошо: generic
type Response<T> = { data: T; status: number };
- Использовать type alias для объектов вместо interface — если нужно расширение (extends) или declaration merging, лучше interface
Практика
- Создайте generic type
Result<T, E>— union из{ ok: true; value: T }и{ ok: false; error: E } - Напишите рекурсивный тип
Jsonдля произвольных JSON-данных - Создайте брендированные типы
UserIdиPostId - Реализуйте
DeepPartial<T>для глубокой опциональности - Напишите type alias для функции-мидлвара:
(req, res, next) => void
Связанные темы
- Интерфейсы — альтернатива для объектных типов
- Type vs Interface — когда что использовать
- Generics — обобщённые типы подробно
- Conditional types — условные типы
- Mapped types — маппированные типы