← Назад к вопросам

Что такое пересечение типов?

2.0 Middle🔥 121 комментариев
#TypeScript

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое пересечение типов

Пересечение типов (type intersection) — это TypeScript операция, которая объединяет несколько типов в один, содержащий все свойства и методы каждого из них. Обозначается символом &.

Основное понятие

Пересечение типов создаёт новый тип, который одновременно является:

  • Первым типом
  • Вторым типом
  • Третьим типом
  • И так далее
type A = {
  name: string;
};

type B = {
  age: number;
};

// Пересечение типов A и B
type Person = A & B;

// Эквивалентно:
type Person = {
  name: string;
  age: number;
};

const person: Person = {
  name: "Иван",
  age: 30
};

Отличие от Union Types

Union types (|) — это логическое ИЛИ:

type A = { x: number };
type B = { y: string };
type Union = A | B;  // либо A, либо B (но не оба)

const obj1: Union = { x: 5 };      // OK
const obj2: Union = { y: "hi" };   // OK
const obj3: Union = { x: 5, y: "hi" }; // OK (но избыточно)

Intersection types (&) — это логическое И:

type A = { x: number };
type B = { y: string };
type Intersection = A & B;  // одновременно и A, и B

const obj: Intersection = { x: 5, y: "hi" };  // OK
// Оба свойства ОБЯЗАТЕЛЬНЫ

Практические примеры

// Пример 1: Расширение интерфейсов
type Identifiable = {
  id: number;
};

type Timestamped = {
  createdAt: Date;
  updatedAt: Date;
};

type User = Identifiable & Timestamped & {
  name: string;
  email: string;
};

const user: User = {
  id: 1,
  createdAt: new Date(),
  updatedAt: new Date(),
  name: "Мария",
  email: "maria@example.com"
};

Пересечение интерфейсов с interface

Те же операции работают с interface (хотя для них есть наследование):

interface HasId {
  id: number;
}

interface HasName {
  name: string;
}

interface HasTimestamp {
  timestamp: Date;
}

type Document = HasId & HasName & HasTimestamp;

const doc: Document = {
  id: 1,
  name: "Report",
  timestamp: new Date()
};

Но с интерфейсами лучше использовать наследование:

interface Document extends HasId, HasName, HasTimestamp {
  // дополнительные свойства
}

Пересечение примитивных типов

Пересечение примитивов создаёт never (невозможный тип):

type Impossible = string & number; // тип never

const value: Impossible = ???; // ошибка: невозможно присвоить

Так как значение не может быть одновременно строкой И числом.

Сложные примеры

// Пример: Mixins
type Loggable = {
  log: () => void;
};

type Serializable = {
  serialize: () => string;
};

type Observable = {
  subscribe: (callback: () => void) => void;
};

type FullFeatured = Loggable & Serializable & Observable;

const obj: FullFeatured = {
  log() { console.log("log"); },
  serialize() { return JSON.stringify({}); },
  subscribe(cb) { cb(); }
};

Пересечение объектов с конфликтом

Если типы имеют одинаковое свойство с разными типами:

type A = { prop: string };
type B = { prop: number };

type Conflict = A & B;  // prop: string & number = never

// Это создаёт конфликт
const obj: Conflict = {
  prop: ???  // ошибка: ни string, ни number не подходят
};

Практическое применение: Декораторы и HOC

// Props базового компонента
type BaseProps = {
  className?: string;
  style?: React.CSSProperties;
};

// Props с функциональностью
type WithLogging = {
  onLog: (message: string) => void;
};

type WithValidation = {
  validate: () => boolean;
};

// Комбинированный тип для компонента
type EnhancedComponentProps = BaseProps & WithLogging & WithValidation;

function MyComponent(props: EnhancedComponentProps) {
  return <div className={props.className}>content</div>;
}

Generic пересечения

function merge<T, U>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 } as T & U;
}

const a = { x: 1 };
const b = { y: "hello" };

const merged = merge(a, b);
// merged имеет тип: { x: number } & { y: string }
console.log(merged.x);     // 1
console.log(merged.y);     // "hello"

Отличие от наследования

// Через наследование
interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

// Через пересечение типов
type Animal = { name: string };
type Dog = Animal & { breed: string };

// Результат один и тот же, но пересечение более гибкое

Лучшие практики

  1. Используйте для комбинирования независимых функциональностей
type Component = Renderable & Clickable & Hoverable;
  1. Предпочитайте наследование для иерархий
interface Parent { }
interface Child extends Parent { }
  1. Избегайте конфликтов типов — проверяйте совместимость

  2. Документируйте сложные пересечения — может быть не очевидно

Итог: пересечение типов — это мощный инструмент для комбинирования типов и создания сложных структур данных в TypeScript.