Исключаем поля из типа в TypeScript: простое руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Как исключить свойство из типа в TypeScript: спасаем код от лишнего багажа

Вы когда-нибудь сталкивались с ситуацией, когда у вас есть большой интерфейс или тип, а вам нужно создать новый тип, который почти такой же, но без одного-двух полей? Дублировать весь код? Копировать 15 строк и удалить одну? Есть способ элегантнее. Сегодня мы разберём, как в TypeScript исключить свойство из типа — это одна из самых популярных утилит, которая спасает от дублирования и делает код чище.

Эта тема — настоящий хит на Stack Overflow. Вопрос "Exclude property from type" набирает тысячи просмотров, потому что рано или поздно каждый разработчик сталкивается с необходимостью удалить поле из типа или исключить свойство из интерфейса. Давайте разбираться по порядку, начиная с простых примеров и заканчивая продвинутыми техниками.

Проблема: зачем исключать поля из типа?

Представьте, что у вас есть интерфейс User:

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  createdAt: Date;
}

Вы хотите создать форму регистрации, где пользователь вводит имя, email и пароль. Но id генерируется на сервере, а createdAt — автоматически. Вам нужен тип NewUser, который содержит только name, email и password. Как это сделать?

Плохое решение — объявить новый интерфейс с нуля:

interface NewUser {
  name: string;
  email: string;
  password: string;
}

Это работает, но нарушает принцип DRY (Don't Repeat Yourself). Если мы изменим тип name в User, придётся менять и в NewUser. Именно здесь на помощь приходит исключение свойств из типа.

Утилитарный тип Omit: главный инструмент

TypeScript предоставляет встроенный утилитарный тип Omit<Type, Keys>. Он создаёт новый тип, исключая указанные ключи из исходного. Синтаксис простой:

type NewUser = Omit<User, 'id' | 'createdAt'>;

Теперь NewUser содержит только name, email и password. Если мы изменим email на Email в User, NewUser подхватит это автоматически.

Давайте проверим на практике:

const newUser: NewUser = {
  name: 'Анна',
  email: 'anna@example.com',
  password: 'secure123'
}; // ✅ Всё ок

const invalidUser: NewUser = {
  name: 'Анна',
  email: 'anna@example.com',
  password: 'secure123',
  id: 1 // ❌ Ошибка: id не существует в типе NewUser
};

Обратите внимание: Omit работает как с интерфейсами, так и с типами. Это универсальный инструмент для исключения полей из типа.

Как работает Omit под капотом?

Чтобы лучше понять, как TypeScript исключает свойства из типа, давайте заглянем под капот. Omit — это комбинация двух других утилит: Pick и Exclude.

Вот как он определён в стандартной библиотеке TypeScript:

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

Разберём по шагам:

  • keyof T — получает все ключи типа T (например, 'id' | 'name' | 'email' | 'password' | 'createdAt')
  • Exclude<keyof T, K> — исключает из этих ключей те, что указаны в K (получаем 'name' | 'email' | 'password')
  • Pick<T, ...> — выбирает из T только оставшиеся ключи

Таким образом, исключение свойства из интерфейса сводится к фильтрации ключей. Элегантно, правда?

Pick: обратная сторона Omit

Иногда проще не исключать, а наоборот — выбрать только нужные поля. Для этого существует утилита Pick<Type, Keys>:

type UserPublic = Pick<User, 'name' | 'email'>;
// Результат: { name: string; email: string; }

Когда использовать Pick, а когда Omit?

  • Omit — если нужно убрать 1-2 поля из большого набора
  • Pick — если нужно оставить 1-2 поля из большого набора

Например, для исключения поля из типа пароля из объекта пользователя удобнее Omit<User, 'password'>, чем перечислять все остальные поля через Pick.

Exclude: утилита для union-типов

Не путайте Exclude (удаление из объединения) с Omit (удаление из объекта). Exclude<UnionType, ExcludedMembers> работает с union-типами (строки, числа, литералы):

type Roles = 'admin' | 'user' | 'guest';
type WithoutAdmin = Exclude<Roles, 'admin'>; // 'user' | 'guest'

Это полезно, когда нужно исключить свойство из типа на уровне строковых литералов. Например, для фильтрации ролей или статусов.

Практический пример: API-ответы

Представьте, что вы работаете с API, который возвращает объект Product:

interface Product {
  id: number;
  title: string;
  price: number;
  description: string;
  category: string;
  image: string;
  rating: {
    rate: number;
    count: number;
  };
}

Для списка товаров на главной странице вам не нужны description и rating. Создайте тип для списка:

type ProductPreview = Omit<Product, 'description' | 'rating'>;

Теперь можно безопасно использовать ProductPreview в компоненте карточки товара, не боясь случайно передать лишние данные.

А для формы редактирования товара может понадобиться исключить id (он не редактируется):

type ProductInput = Omit<Product, 'id' | 'rating'>;

Продвинутые техники: условные типы и кастомные утилиты

Иногда в

Блоги

Рекомендаций по книгам