Как исключить свойство из типа в TypeScript?
Используйте утилиту Omit — это то, что вам нужно
В TypeScript есть встроенная утилита Omit<T, K>, которая создаёт новый тип, исключая из исходного типа T свойства, перечисленные в K. Это идеальное решение для вашей задачи, так как вам не нужно перечислять все оставшиеся поля — достаточно указать только то, что нужно исключить.
// Исходный интерфейс
interface User {
id: number;
name: string;
email: string;
password: string;
createdAt: Date;
}
// Новый тип без password
type UserWithoutPassword = Omit<User, 'password'>;
// Пример использования
const user: UserWithoutPassword = {
id: 1,
name: 'Alice',
email: 'alice@example.com',
createdAt: new Date(),
// password: 'secret' // Ошибка! Это свойство исключено
};
Как это работает под капотом
Утилита Omit использует комбинацию других встроенных утилит: Pick и Exclude. Её реализация выглядит так:
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
keyof T— получает объединение всех ключей типа T (например,'id' | 'name' | 'email' | 'password' | 'createdAt').Exclude<...>— исключает из этого объединения указанные ключи (например,'password').Pick<T, ...>— выбирает из T только те свойства, которые остались после исключения.
Преимущества использования Omit
- DRY-подход: вы не дублируете поля интерфейса.
- Гибкость: можно исключить несколько свойств сразу:
Omit<User, 'password' | 'createdAt'>. - Безопасность типов: TypeScript проверит, что исключаемое свойство действительно существует в исходном типе.
Другие полезные приёмы
Если вам нужно исключить свойство по условию (например, все свойства определённого типа), можно написать свой собственный тип:
// Исключить все строковые свойства
type WithoutStringProps<T> = {
[K in keyof T as T[K] extends string ? never : K]: T[K];
};
type UserWithoutStrings = WithoutStringProps<User>;
// Результат: { id: number; createdAt: Date }
Но для вашей задачи Omit — самый простой и правильный вариант.
Альтернатива: используйте Pick с явным перечислением
Хотя Omit удобнее, иногда Pick может быть предпочтительнее, если вы хотите явно указать, какие поля оставить, а не исключать. Например, если интерфейс может измениться в будущем, и вы хотите гарантировать, что новый тип содержит только определённые поля, даже если в исходном интерфейсе появятся новые.
type UserPublic = Pick<User, 'id' | 'name' | 'email'>;
Когда использовать Pick вместо Omit
- Когда вы хотите защититься от добавления новых полей в исходном типе (они автоматически не попадут в новый тип).
- Когда нужно оставить только небольшое подмножество полей (например, 3 из 20).
- Для лучшей читаемости: явно видно, какие поля доступны.
Комбинированный подход
Вы также можете создать свой тип-утилиту, который исключает несколько свойств, но при этом явно указывает оставшиеся:
type ExcludeAndPick<T, Excluded extends keyof T, Picked extends keyof T> = Pick<T, Picked> & Omit<T, Excluded>;
// Но это избыточно — проще использовать Omit или Pick по отдельности.
В большинстве случаев Omit — это то, что вы ищете. Но помните о Pick как о полезной альтернативе для конкретных сценариев.