Mastering TypeScript’s `Extract<>`: Selective Type Extraction for Better Code
Posted by Nuno Marques on 31 Jan 2025
TypeScript is well known for its powerful type system, and one of its lesser-known but highly useful utility types is Extract<>
. It allows developers to extract specific values from a larger type, making it perfect for refining and reusing types efficiently.
In this post, we’ll explore:
- What
Extract<>
is and when it was introduced. - Common use cases and real-world scenarios.
- Alternative approaches for similar problems.
- Additional resources and references for further learning.
What is Extract<>
?
The Extract<>
utility type in TypeScript is used to create a new type by extracting only the specified subset from a union type.
It was introduced in TypeScript 2.8 (March 2018) along with other utility types to improve code reusability and maintainability.
Why Use Extract<>
?
Extract<>
is particularly useful when:
- You need to reuse only a few values from a large union type.
- You want to avoid manually redefining subsets of a type.
- You aim to keep types in sync and prevent redundant declarations.
Let’s see how it works.
How Extract<>
Works
Syntax
type Extract<T, U> = T extends U ? T : never;
It takes two arguments:
T
– the original type (usually a union type).U
– the subset to be extracted.
If T
extends U
, it keeps the value. Otherwise, it removes it.
For official documentation, check out the TypeScript Utility Types.
Real-Life Example: Extracting Avatar Sizes
Suppose you have a component where an avatar can be displayed in multiple sizes:
export type AvatarSize = '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
Now, let’s say you have another component (e.g., a TagAvatar
) that only supports three specific sizes. Instead of redefining them manually, you can use Extract<>
:
export type TagAvatarSize = Extract<AvatarSize, '2xs' | 'xs' | 'sm'>;
This creates a new type:
type TagAvatarSize = '2xs' | 'xs' | 'sm';
Benefits:
- Ensures consistency with
AvatarSize
. - Eliminates redundant type declarations.
- If
AvatarSize
changes,TagAvatarSize
will stay in sync.
For more TypeScript best practices, visit TypeScript Deep Dive.
More Practical Use Cases
Filtering Union Types
You can use Extract<>
to create a subset of roles in an application:
type UserRole = 'admin' | 'editor' | 'viewer' | 'guest';
type AdminRole = Extract<UserRole, 'admin' | 'editor'>;
Now, AdminRole
includes only 'admin'
and 'editor'
.
Narrowing API Response Types
When working with an API that returns multiple statuses, you may want to handle only specific ones:
type APIResponseStatus = 'success' | 'error' | 'pending' | 'timeout';
type ErrorStates = Extract<APIResponseStatus, 'error' | 'timeout'>;
ErrorStates
includes only 'error'
and 'timeout'
.
Handling Event Types
If your application has multiple event types but a certain function only needs a subset:
type EventType = 'click' | 'hover' | 'keydown' | 'keyup' | 'scroll';
type KeyboardEvent = Extract<EventType, 'keydown' | 'keyup'>;
KeyboardEvent
will only contain 'keydown' | 'keyup'
.
For additional TypeScript patterns, check out TypeScript Utility Types Cheat Sheet.
Extract<>
vs. Similar Utility Types
| Utility Type | Purpose |
| --------------- | ------------------------------------------------ |
| Extract<T, U>
| Extracts specific values from a union type. |
| Exclude<T, U>
| Removes specified values from a union type. |
| Pick<T, K>
| Picks specific properties from an object type. |
| Omit<T, K>
| Removes specific properties from an object type. |
Comparison: Extract vs. Exclude
While Extract<>
keeps the selected values, Exclude<>
removes them:
type Size = 'small' | 'medium' | 'large' | 'xlarge';
type SmallSizes = Extract<Size, 'small' | 'medium'>; // 'small' | 'medium'
type LargeSizes = Exclude<Size, 'small' | 'medium'>; // 'large' | 'xlarge'
Alternative Solutions
While Extract<>
is an elegant solution, here are a couple of alternatives:
Manual Redefinition (Not Recommended)
type TagAvatarSize = '2xs' | 'xs' | 'sm';
Problem: If AvatarSize
changes, you must manually update TagAvatarSize
.
Intersection with Union (Works but Less Readable)
type TagAvatarSize = '2xs' | 'xs' | 'sm' & AvatarSize;
Problem: More complex to read compared to Extract<>
.
Mapped Types (Works for Object Properties)
For object-based filtering:
type FullUser = { id: number; name: string; role: 'admin' | 'user' | 'guest' };
type AdminUser = Pick<FullUser, 'id' | 'role'>;
AdminUser
keeps only id
and role
.
For more details on mapped types, refer to TypeScript Mapped Types.
Conclusion
Extract<>
is a simple but powerful tool in TypeScript that helps you:
- Reuse types efficiently.
- Keep your code DRY and maintainable.
- Prevent errors when type definitions change.
Whenever you need to filter out a specific subset from a union type, Extract<>
is your go-to utility. Give it a try in your next TypeScript project.
For more TypeScript tips, explore: