Suggest an editImprove this articleRefine the answer for “Utility type exclude in TypeScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`Exclude<T, U>`** removes from a union type all members assignable to `U`. ```ts type Status = 'ok' | 'error' | null; type Clean = Exclude<Status, null>; // 'ok' | 'error' ``` **Key point:** implemented as `T extends U ? never : T`, so TypeScript checks each union member separately and drops those that match.Shown above the full answer for quick recall.Answer (EN)Image**`Exclude<T, U>`** is a TypeScript utility type that removes from a union all members assignable to `U`, leaving only what does not match. ## Theory ### TL;DR - `Exclude<T, U>` filters a union by removing members that match `U` - Think of `T` as a full set of values, `U` as the ones you want gone - Opposite of [`Extract`](/questions/utility-type-extract-in-typescript), which keeps only matching members - Implemented as `T extends U ? never : T` - If all members of `T` match `U`, the result is `never` ### Quick example ```ts type Status = 'active' | 'inactive' | 'banned' | 'deleted'; // Remove statuses you don't need in this context type VisibleStatus = Exclude<Status, 'banned' | 'deleted'>; // Result: 'active' | 'inactive' ``` Two members are gone. The rest survive. ### How the compiler handles this `Exclude` is defined in TypeScript's standard library as a distributive conditional type: ```ts type Exclude<T, U> = T extends U ? never : T; ``` TypeScript applies this check to each union member separately. For `Exclude<'a' | 'b' | 'c', 'a'>`: 1. `'a' extends 'a'` → `never` 2. `'b' extends 'a'` → `'b'` 3. `'c' extends 'a'` → `'c'` The results merge: `never | 'b' | 'c'`, which simplifies to `'b' | 'c'`. TypeScript drops `never` from unions automatically. ### Exclude vs Extract These two are mirrors of each other. | Utility | What it does | |---|---| | `Exclude<T, U>` | Removes from `T` all members assignable to `U` | | `Extract<T, U>` | Keeps in `T` only members assignable to `U` | ```ts type Mixed = string | number | boolean; type OnlyStrings = Exclude<Mixed, number | boolean>; // string type NumbersAndBools = Extract<Mixed, number | boolean>; // number | boolean ``` Same input, opposite results. ### When to use - Strip `null` and `undefined` from a union before working with values (though [`NonNullable`](/questions/utility-type-nonnullable-in-typescript) is shorter for this specific case) - Narrow API response types before passing them to a handler function - Filter string literal unions: event names, action types, status codes - Build a subset of a large role or status union for a specific context ### Common mistakes **Confusing `Exclude` with [`Omit`](/questions/utility-type-omit-in-typescript).** They sound similar but target different things. `Omit` removes keys from an object type. `Exclude` removes members from a union. ```ts // Wrong: won't remove a property from an object type type WithoutKind = Exclude<{ kind: 'circle'; size: number }, 'kind'>; // Still { kind: 'circle'; size: number } - nothing changed // Right: use Omit for object keys type WithoutKind2 = Omit<{ kind: 'circle'; size: number }, 'kind'>; // { size: number } ``` **Typos in string literals.** If the value in `U` does not match anything in `T`, nothing gets removed and TypeScript gives no error. ```ts type Roles = 'admin' | 'editor' | 'viewer'; // Typo: 'editer' matches nothing in the union type Result = Exclude<Roles, 'editer'>; // 'admin' | 'editor' | 'viewer' - unchanged ``` No warning, no error. I've seen this eat 20 minutes of debugging in real codebases. Always double-check spelling in `U`. **Expecting `Exclude` to filter by object properties.** It distributes over union members, not over fields inside a single object. ```ts type User = { role: 'admin' | 'viewer' }; // Won't filter by .role - the whole User object is the union member type AdminOnly = Exclude<User, { role: 'viewer' }>; // Still User ``` If you need to filter object unions this way, each variant must be a separate union member, not a property inside one type. ### Real-world usage - Redux/Zustand: exclude `'INIT'` or `'RESET'` from the action union when writing specific slice handlers - React props: narrow a string literal prop in a child component without redefining the full type - Form state: `Exclude<FieldStatus, 'untouched'>` to type only fields the user has interacted with - API client: strip `null | undefined` from a response union before passing data to a parser ### Follow-up questions **Q:** What is the internal definition of `Exclude` in TypeScript? **A:** `type Exclude<T, U> = T extends U ? never : T`. It is a distributive conditional type, so TypeScript applies the check per union member, not to the union as a whole. **Q:** What is the difference between `Exclude` and `Omit`? **A:** `Exclude` works on union members, `Omit` works on object keys. `Exclude<'a' | 'b', 'a'>` gives `'b'`. `Omit<{ a: 1; b: 2 }, 'a'>` gives `{ b: 2 }`. **Q:** What does `Exclude<string, string>` return? **A:** `never`. Every member of `T` is assignable to `U`, so all members map to `never`. An empty union collapses to `never` in TypeScript. **Q:** What happens to distribution if `T` is wrapped in a tuple? **A:** Distribution stops. `[T] extends [U]` checks the tuple as a whole, not each union member separately. This is a common edge case when writing advanced generic utilities that intentionally suppress distribution. ## Examples ### Filtering HTTP methods ```ts type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS'; // Keep only read-only methods type SafeMethod = Exclude<HttpMethod, 'POST' | 'PUT' | 'DELETE' | 'PATCH'>; // 'GET' | 'OPTIONS' function readOnly(method: SafeMethod, url: string) { return fetch(url, { method }); } readOnly('GET', '/api/users'); // ok readOnly('POST', '/api/users'); // TS error: 'POST' is not assignable to SafeMethod ``` Define the full set once, then carve out subsets where needed. No duplication. ### Removing null from an API response type ```ts type ApiStatus = 'ok' | 'error' | 'pending' | null | undefined; // After the response resolves, null and undefined are no longer possible type ResolvedStatus = Exclude<ApiStatus, null | undefined>; // 'ok' | 'error' | 'pending' function handleResolved(status: ResolvedStatus) { if (status === 'ok') { console.log('Request succeeded'); } } ``` `NonNullable<ApiStatus>` produces the same result here. Both are correct. `NonNullable` is a named shortcut for this specific pattern.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.