NIXX/DEVv1.14.0
ArticlesFavorites
Sign In
Sign In
Articles

Welcome to our blog

A curated collection of insightful articles, practical guides, and expert tips designed to simplify your workflow

Cover image for: How to Make the Most of TypeScript’s Utility Types
August 7, 20255 MIN READ min readBy ℵi✗✗

How to Make the Most of TypeScript’s Utility Types

Learn how to simplify your code and reduce repetition using built-in TypeScript utility types like Partial, Pick, Omit, ReturnType, and more.

webdevproductivitytypescript
ℵi✗✗

ℵi✗✗

Full-Stack Developer

Passionate about building tools and sharing knowledge with the developer community.

Was this helpful?

Popular Posts

  • NixOS vs. Arch Linux: Which One Belongs in Your Dev Setup?

    NixOS vs. Arch Linux: Which One Belongs in Your Dev Setup?

    5 MIN READ min read

  • How to Enable HTTPS on Localhost in Under 2 Minutes

    How to Enable HTTPS on Localhost in Under 2 Minutes

    3 MIN READ min read

  • Migrating from Create React App (CRA) to Vite: A Step-by-Step Guide

    Migrating from Create React App (CRA) to Vite: A Step-by-Step Guide

    4 MIN READ min read

  • Array Destructuring in PHP: A Practical Guide for Modern Developers

    Array Destructuring in PHP: A Practical Guide for Modern Developers

    5 MIN READ min read

Recommended Products

  • Apple iPad (7th Gen)

    Apple iPad (7th Gen)

    4.3
  • Fitbit Versa 4

    Fitbit Versa 4

    4.3
  • JBL Flip 6

    JBL Flip 6

    4.8
  • Dell 24 Monitor — SE2425HM Full HD

    Dell 24 Monitor — SE2425HM Full HD

    4.7

May contain affiliate links

Topics

webdev33productivity16cybersecurity12javascript11automation9guide8react7typescript7php6tutorial6freelancing5github actions5privacy5how to4Node.js4
+111 more topics →
🇺🇸USD ACCOUNTOpen a free US-based USD accountReceive & save in USD — powered by ClevaSponsoredInterserver Hosting#1 VALUEAffordable, reliable hosting from $2.50/mo99.9% uptimeSponsored

TypeScript's utility types are one of its most practical features and one of the most commonly overlooked. Rather than defining new types from scratch every time a variation of an existing type is needed, utility types provide a set of built-in transformations: make all fields optional, exclude certain keys, extract a return type from a function, and so on.

This guide covers the most useful utility types with specific examples and the situations where each one is the right tool.

What this covers:

  • Partial and Required for optional and required field control

  • Pick and Omit for selecting and excluding fields

  • Readonly for immutable types

  • Record for key-value mappings

  • ReturnType and Parameters for function type extraction

  • NonNullable for excluding null and undefined

  • Combining utility types for more complex transformations


The Base Type Used in Examples

Most examples in this guide use a common User type for consistency:

type User = {
    id: number;
    name: string;
    email: string;
    role: 'admin' | 'editor' | 'viewer';
};

Partial<Type>

Makes all properties of a type optional. Every field becomes field?: type rather than field: type.

const updateUser = (id: number, updates: Partial<User>): void => {
    // updates may contain any subset of User fields
};

updateUser(1, { name: "Ada" });           // valid
updateUser(1, { email: "[email protected]" });   // valid
updateUser(1, { name: "Ada", role: "editor" }); // valid

This is most useful for update and patch endpoints where only a subset of fields is being modified. Without Partial, the function would require the full User object even for single-field updates.


Required<Type>

The inverse of Partial. Makes all properties required, removing any ? from the type definition.

type Config = {
    cache?: boolean;
    retries?: number;
    timeout?: number;
};

type StrictConfig = Required<Config>;
// { cache: boolean; retries: number; timeout: number }

Useful when a type is defined with optional fields for flexibility during construction, but a function downstream needs to guarantee all fields are present before proceeding.


Pick<Type, Keys>

Creates a new type containing only the specified keys from an existing type.

type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string }

A common use case is building view models or API response shapes that expose only the fields a particular consumer needs, without defining a separate interface manually.

function getUserSummaries(users: User[]): Pick<User, 'id' | 'name'>[] {
    return users.map(({ id, name }) => ({ id, name }));
}

Omit<Type, Keys>

Creates a new type by removing the specified keys from an existing type. The counterpart to Pick.

type PublicUser = Omit<User, 'email'>;
// { id: number; name: string; role: 'admin' | 'editor' | 'viewer' }

Useful for stripping sensitive or internal fields before returning data to a client, or for creating a base type that will be extended with different field sets.

type NewUser = Omit<User, 'id'>;
// id is assigned by the database, not provided by the caller

Readonly<Type>

Makes all properties of a type read-only. Assignments to any property after initialization produce a type error.

const config: Readonly<Config> = {
    cache: true,
    retries: 3,
    timeout: 5000,
};

config.retries = 5; // Error: Cannot assign to 'retries' because it is a read-only property

This is a compile-time enforcement only. Readonly does not use Object.freeze and does not prevent mutation at runtime. For deep immutability (nested objects), a recursive DeepReadonly type is needed, which TypeScript does not provide as a built-in but can be defined manually.


Record<Keys, Type>

Constructs a type with a set of keys mapped to a specified value type. Both the keys and the value type are explicit.

type Permission = 'read' | 'write' | 'delete';

type PermissionMap = Record<Permission, boolean>;

const userPermissions: PermissionMap = {
    read: true,
    write: true,
    delete: false,
};

Record enforces that all keys in the union are present. Omitting delete from userPermissions would be a type error, which prevents accidentally incomplete mappings.

It also works with string or number as the key type for more open-ended maps:

type Cache = Record<string, User>;

ReturnType<Function>

Extracts the return type of a function type.

function getUser() {
    return { id: 1, name: "Jane", role: "editor" as const };
}

type UserResult = ReturnType<typeof getUser>;
// { id: number; name: string; role: "editor" }

This is particularly useful when the return type of a function is complex or computed, and defining it separately would mean maintaining two things that must stay in sync. ReturnType keeps the type derived from the function automatically.

It also works with generic and async functions:

async function fetchUser(id: number) {
    return { id, name: "Jane" };
}

type FetchResult = Awaited<ReturnType<typeof fetchUser>>;
// { id: number; name: string }

Awaited unwraps the Promise from the return type of an async function.


Parameters<Function>

Extracts the parameter types of a function as a tuple.

function createUser(name: string, role: 'admin' | 'editor', active: boolean) {
    // ...
}

type CreateUserParams = Parameters<typeof createUser>;
// [name: string, role: 'admin' | 'editor', active: boolean]

Useful when a wrapper function needs to accept the same arguments as the wrapped function without duplicating the type definition.


NonNullable<Type>

Removes null and undefined from a type.

type MaybeUser = User | null | undefined;

type DefiniteUser = NonNullable<MaybeUser>;
// User

Often used after a null check to narrow a type for a section of code that has already verified the value is present, or when constructing a type from a union that may have been defined to allow nullability.


Combining Utility Types

Utility types compose naturally. Complex type transformations can be expressed by chaining them:

// A type for updating a user: all User fields optional, but id is excluded
type UserUpdate = Partial<Omit<User, 'id'>>;
// { name?: string; email?: string; role?: 'admin' | 'editor' | 'viewer' }
// A readonly version of only the public fields
type PublicReadonlyUser = Readonly<Pick<User, 'id' | 'name' | 'role'>>;

The readability of composed utility types degrades as more are nested. When a composed type is reused in multiple places, assigning it a named alias keeps the code readable and makes the intent explicit.


Key Takeaways

  • Partial makes all fields optional; Required makes all fields mandatory. Both are commonly used at API boundaries where field presence varies.

  • Pick creates a type from a subset of keys; Omit creates a type with certain keys removed. Use them to shape types for specific consumers without defining separate interfaces.

  • Readonly enforces compile-time immutability but does not prevent runtime mutation. It applies to the top-level properties only.

  • Record maps a union of keys to a value type and enforces that all keys in the union are present.

  • ReturnType and Parameters extract types from functions, keeping derived types automatically in sync when the function signature changes.

  • NonNullable strips null and undefined from a union type.

  • Utility types compose. Combining two or three produces precize transformations that would otherwize require manual type definitions.


Conclusion

Utility types are the mechanism TypeScript provides for deriving types from existing ones rather than defining everything from scratch. Each one addresses a specific and recurring transformation: making fields optional, excluding sensitive keys, enforcing immutability, mapping a fixed set of keys. Used consistently, they reduce the amount of type maintenance required as a codebase grows and keep related types automatically synchronized with their sources.


Using a utility type in an interesting or non-obvious way? Share it in the comments.

Topics
webdevproductivitytypescript

Discussion

Join the discussion

Sign in to share your thoughts and engage with the community.

Sign In
Loading comments…

Continue Reading

More Articles

View all
Cover image for: Embedding Cybersecurity in Development: Best Practices for 2025
Jul 1, 20257 MIN READ min read

Embedding Cybersecurity in Development: Best Practices for 2025

A developer-focused guide to integrating security into your workflow—covering tools, practices, and mindset shifts for 2025.

Cover image for: AI for DevOps: Tools That Are Already Changing the Game
Jun 17, 20256 MIN READ min read

AI for DevOps: Tools That Are Already Changing the Game

How artificial intelligence is transforming CI/CD pipelines, monitoring, and incident response—today.

Cover image for: Array Destructuring in PHP: A Practical Guide for Modern Developers
Mar 12, 20255 MIN READ min read

Array Destructuring in PHP: A Practical Guide for Modern Developers

From PHP 7.1 to 8.1—learn how array destructuring simplifies variable assignment, reduces boilerplate, and improves readability in modern PHP development.

Cover image for: How Much Does Business Email Really Cost? (And How to Save Money)
May 25, 20254 MIN READ min read

How Much Does Business Email Really Cost? (And How to Save Money)

If you're paying for business email through Google Workspace or Microsoft 365, you might be overpaying. Here's how to rethink your setup and save hundreds per year.

|Made with · © 2026|TermsPrivacy
AboutBlogContact

Free, open-source tools for developers and creators · Community driven