Mastering TypeScript’s `Extract<>`: Selective Type Extraction for Better Code

Blog

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: