You’re Using TypeScript Wrong Without These Utilities

Jiya AgrawalJiya Agrawal
4 min read

TypeScript? Yeah, we all use it daily.
Defining props for a component, typing an API response, making sure no one passes string where a number belongs — all good.

But here’s the thing:

  • What if you only want to use part of a type?
    → You’d probably create a new one, right?

  • What if you want to use the whole type except one field?
    → Yep, another new one.

  • What if you want the type but with everything optional?
    → You guessed it… another type.

Feels like we’re constantly cloning types just to tweak them a little.

What if you didn’t have to create new ones every time?
Sounds nice, right?

That’s where TypeScript Utility Types come in. They’re like shortcuts that let you reuse and transform existing types instead of reinventing them.

Let’s look at the ones I (and probably you) will actually use every day:

  • Partial

  • Pick

  • Omit

  • Readonly

  • Record

1. Partial<T> – When You Don’t Need Everything

Normally, a type forces you to provide all its properties. But in real projects, that’s not always how data flows. For example, when you’re updating a user profile, you might only have one or two fields to update - not the whole object.

That’s where Partial comes in. It makes every property optional, so you can pass only what you need.

type User = { 
  id: number; 
  name: string; 
  email: string; 
  isAdmin: boolean;
};

const updateUser = (id: number, data: Partial) => { 
    // data could be { name: "Alice" } // or { email: "alice@example.com" }
    console.log(`Updating user ${id} with data:`, data);
};

You can use Partial mostly for:

  • PATCH API requests (where you only send the changed fields)

  • Form updates (user changes only name, not email)

  • Optional configs (default values filled in later)

2. Pick<T, K> – Only Grab What You Need

Sometimes you don’t want the entire object - just a subset. That’s what Pick does: it extracts only the keys you care about and ignores the rest.

This is super handy for things like UI components. For example, a user card component doesn’t need admin rights, just basic info.

type User = {
  id: number;
  name: string;
  email: string;
  isAdmin: boolean;
};

type UserCardProps = Pick<User, "name" | "email">;

const UserCard = ({ name, email }: UserCardProps) => (
  <div>
    <h2>{name}</h2>
    <p>{email}</p>
  </div>
);

You can use Pick when:

  • Passing props to components (only what’s needed)

  • Creating lightweight response objects from APIs

  • Avoiding duplication of types while working with forms or DTOs

3. Omit<T, K> – Hide What You Don’t Want

Sometimes the opposite problem happens: you want everything except a few fields. That’s where Omit comes in.

It’s perfect for situations where you don’t want to expose sensitive data (like password or isAdmin) in an API response, but still want the rest of the object.

type User = {
  id: number;
  name: string;
  email: string;
  password: string;
  isAdmin: boolean;
};

type PublicUser = Omit<User, "password" | "isAdmin">;

const getPublicUser = (user: User): PublicUser => {
  const { password, isAdmin, ...rest } = user;
  return rest;
};

I use Omit mostly for:

  • Sanitizing API responses (never send passwords/tokens back)

  • Building public-facing models from internal data

  • Avoiding duplication while still maintaining type safety

4. Readonly<T> – Lock It Down

There are some objects in your app you never want to change after creation - configs, constants, environment settings, etc. Readonly makes every property immutable, so you can’t accidentally overwrite it.

type Config = {
  appName: string;
  version: string;
};

const config: Readonly<Config> = {
  appName: "MyApp",
  version: "1.0.0",
};

config.version = "2.0.0"; // ❌ Error

I use Readonly for:

  • Configuration objects (like app settings, API base URLs)

  • Constants that should never be reassigned

  • Preventing bugs where devs accidentally overwrite data

5. Record<K, T> – Build Clean Mappings

Record is like a type-safe dictionary. It lets you define a set of keys and the type of their values. This makes it great for creating lookup tables, enums with data, or permission maps.

type Role = "admin" | "user" | "guest";

const rolePermissions: Record<Role, string[]> = {
  admin: ["read", "write", "delete"],
  user: ["read", "write"],
  guest: ["read"],
};

💡 I use Record when:

  • Defining permissions or role-based access

  • Building feature flag systems

  • Creating lookup tables (like country codes → country names)

TL;DR

Instead of constantly creating new types, just to tweak an existing one. TypeScript gives you shortcuts for this:

  • Partial → updates & optional configs

  • Pick → select exactly what you need

  • Omit → exclude sensitive/unwanted fields

  • Readonly → lock down constants

  • Record → clean mappings, type-safe dictionaries

Once you start using them, you’ll wonder why you ever copied and pasted whole types before.

If you enjoyed this article and want to discover more such lesser-known but powerful JavaScript, TypeScript, ReactJS, NextJS features, follow me for more insights.
LinkedIn
Twitter

0
Subscribe to my newsletter

Read articles from Jiya Agrawal directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jiya Agrawal
Jiya Agrawal