Extract<Type, Union>
Keeps only the members of a union that are assignable to a second type.
type Extract<Type, Union> = Type extends Union ? Type : never- Added in TypeScript 2.8
Parameters
| Parameter | Type | Description |
|---|---|---|
$Type | any type | The union to filter. It appears naked on the left of the conditional, so the check distributes over each member. |
$Union | any type | The pattern to match. Members assignable to it are kept; a partial object type such as `{ kind: "circle" }` matches any variant carrying that tag. |
Return value
The union of members of Type assignable to UnionA union containing each member of Type that is assignable to Union; `never` if none are.
Errors
Never errors. A pattern that matches nothing produces `never`, and the error surfaces where the result is used.
Extract is the complement of Exclude: same distributive conditional, opposite branches. It keeps the union members that are assignable to the second argument and discards the rest.
The main use: pulling a variant out by its tag
Given a discriminated union, Extract<Event, { kind: "click" }> yields exactly the click variant, with all of its own fields. This matters because the alternative — writing the variant out again as a standalone interface — creates a second definition that will drift. With Extract there is one source of truth, and adding a field to the click variant updates the extracted type for free.
What trips people up
Matching is by assignability, and it is not symmetric. { kind: "click"; x: number } is assignable to { kind: "click" } because it has at least those members, so the narrow pattern matches the wide variant. Swap the arguments and you get never.
A wide second argument keeps too much. Extract<"a" | "b", string> is "a" | "b", since both literals are assignable to string.
No match is silent. A tag typo gives never, and the failure appears at the first assignment rather than at the declaration.
Example
When to use it
- Pull one variant out of a discriminated union so a handler can be typed against just that case.
- Intersect two literal unions to find the keys they have in common.
- Keep only the function-typed members of a mixed union before mapping over them.
Frequently asked questions
How do I get one variant of a discriminated union?
Use `Extract<Union, { tag: "value" }>` — the partial object acts as a pattern, and any variant carrying that tag is assignable to it, so exactly that variant is returned with all of its fields.
What is the difference between Extract and Exclude?
They are complements built on the same distributive conditional: `Extract<T, U>` keeps the members of `T` assignable to `U`, while `Exclude<T, U>` drops them. Together they partition the union.
Why does Extract return never?
Because no member of the first union is assignable to the second — most often a typo in a discriminant literal, or arguments passed in the wrong order, since a wide variant is not assignable to a narrow pattern the other way round.
More examples
Type a handler map without redeclaring variants
Prints `[ 'a', 'b', 'c' ]`, `[ 'b' ]`, then `[]`. Each handler receives only its own variant: inside `add`, `action.item` is a `string` and `action.index` is a compile error. `ActionOf` is doing the work — the mapped type feeds each tag through `Extract` so every slot in the map gets a precisely typed parameter, with the variants declared exactly once.
type Action =
| { type: "add"; item: string }
| { type: "remove"; index: number }
| { type: "clear" };
// Given a tag, produce the matching variant.
type ActionOf<T extends Action["type"]> = Extract<Action, { type: T }>;
type Handlers = { [T in Action["type"]]: (action: ActionOf<T>) => string[] };
let items: string[] = ["a", "b"];
const handlers: Handlers = {
add: (action) => [...items, action.item],
remove: (action) => items.filter((_, i) => i !== action.index),
clear: () => [],
};
console.log(handlers.add({ type: "add", item: "c" }));
console.log(handlers.remove({ type: "remove", index: 0 }));
console.log(handlers.clear({ type: "clear" }));Direction of assignability, and the empty result
Logs `red`, then the three-colour array. `Shared` behaves like set intersection because each literal is only assignable to itself; `AllOfThem` keeps everything because every string literal is assignable to `string`, which is the trap when the second argument is a primitive rather than a literal union. `Empty` is `never`, and as the commented line shows, that only reports itself at the point of assignment.
type Colour = "red" | "green" | "blue";
type Warm = "red" | "orange";
type Shared = Extract<Colour, Warm>; // "red"
type AllOfThem = Extract<Colour, string>; // "red" | "green" | "blue"
type Empty = Extract<Colour, number>; // never
const shared: Shared = "red";
const all: AllOfThem[] = ["red", "green", "blue"];
console.log(shared);
console.log(all);
// const nothing: Empty = "red";
// -> Type 'string' is not assignable to type 'never'.
console.log("Empty has no inhabitants");
Discussion