Type Alias

Type alias — именованный тип, создаваемый ключевым словом type. Позволяет дать имя любой комбинации типов.

Зачем нужно

  • Сокращение длинных и сложных типов
  • Переиспользование составных типов
  • Семантическое именование: type UserId = number понятнее чем просто number
  • Создание обобщённых (generic) типов
  • Рекурсивные типы для древовидных структур

Где используется

  • Везде, где нужно дать имя типу: параметры, возвращаемые значения, свойства
  • Union и intersection типы
  • Утилитарные и вспомогательные типы
  • Conditional и mapped types (только через type, не interface)

Предпосылки

Базовый синтаксис

// 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>;

Частые ошибки

  1. Путать type alias и новый тип — type alias это просто имя, не новый тип
type UserId = number;
type OrderId = number;

let userId: UserId = 1;
let orderId: OrderId = userId; // OK! Оба — number, нет защиты
// Для защиты — используйте брендированные типы
  1. Рекурсивный тип без базового случая — приведёт к бесконечной рекурсии
  2. Слишком сложные вложенные generics — ухудшают читаемость и производительность компилятора
  3. Не использовать 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 };
  1. Использовать type alias для объектов вместо interface — если нужно расширение (extends) или declaration merging, лучше interface

Практика

  1. Создайте generic type Result<T, E> — union из { ok: true; value: T } и { ok: false; error: E }
  2. Напишите рекурсивный тип Json для произвольных JSON-данных
  3. Создайте брендированные типы UserId и PostId
  4. Реализуйте DeepPartial<T> для глубокой опциональности
  5. Напишите type alias для функции-мидлвара: (req, res, next) => void

Связанные темы

Ресурсы