Exclude, Extract, NonNullable
Exclude,ExtractиNonNullable— утилитарные типы TypeScript для фильтрации union-типов: Exclude убирает варианты, Extract оставляет только совпадающие, NonNullable удаляетnullиundefined.
Зачем нужно
При работе с union-типами часто нужно получить подмножество вариантов — например, только события определённого типа или строки без null. Эти утилиты позволяют делать это декларативно на уровне типов, без дублирования деклараций.
Где используется
Exclude— убрать из union конкретные варианты (например, убратьnullиundefinedвручную)Extract— извлечь только совпадающие с шаблоном варианты (например, все строки изstring | number | boolean)NonNullable— убратьnull | undefinedиз любого типа (обёртка над Exclude)- Типизация discriminated unions — выбрать только нужные события из общего union
Основной контент
Exclude<T, U>
Убирает из T все варианты, присваиваемые к U.
type T1 = Exclude<string | number | boolean, boolean>;
// string | number
type T2 = Exclude<"a" | "b" | "c", "a" | "c">;
// "b"
type T3 = Exclude<string | null | undefined, null | undefined>;
// string — аналогично NonNullable
// Реализация внутри TS:
// type Exclude<T, U> = T extends U ? never : T;
Extract<T, U>
Оставляет в T только варианты, присваиваемые к U.
type T1 = Extract<string | number | boolean, string | number>;
// string | number
type T2 = Extract<"circle" | "square" | "triangle", "circle" | "square">;
// "circle" | "square"
// Реализация:
// type Extract<T, U> = T extends U ? T : never;
NonNullable<T>
Убирает null и undefined из типа.
type T1 = NonNullable<string | null | undefined>;
// string
type T2 = NonNullable<number | null>;
// number
type T3 = NonNullable<null | undefined>;
// never
// Реализация:
// type NonNullable<T> = T & {};
Практические примеры
// Извлечение конкретных action-типов из union
type Action =
| { type: "FETCH_USER"; id: string }
| { type: "FETCH_ORDER"; id: string }
| { type: "RESET" };
type FetchActions = Extract<Action, { type: `FETCH_${string}` }>;
// { type: "FETCH_USER"; id: string } | { type: "FETCH_ORDER"; id: string }
// Убрать RESET из Action
type NonResetAction = Exclude<Action, { type: "RESET" }>;
// { type: "FETCH_USER"; ... } | { type: "FETCH_ORDER"; ... }
// NonNullable для строгой типизации
type MaybeUser = User | null | undefined;
type DefiniteUser = NonNullable<MaybeUser>; // User
// В generic функции
function compact<T>(arr: (T | null | undefined)): NonNullable<T> {
return arr.filter((x): x is NonNullable<T> => x != null);
}
Комбинирование
// Все ключи объекта кроме функций
type NonFunctionKeys<T> = {
[K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
// Только string-ключи из union
type StringValues = Extract<string | number | boolean | object, string>;
// string
Частые ошибки
- Путать Exclude и Extract — Exclude убирает, Extract оставляет.
- Применять Exclude к объектным типам —
Exclude<{ a: 1 }, { a: 1 }>не уберёт объект (структурная совместимость); лучше использовать с literal union. - Думать что NonNullable = Exclude<T, null> — NonNullable убирает и
null, иundefinedодновременно. - Использовать NonNullable вместо
!оператора — NonNullable работает на уровне типов,!— в рантайме.