DEV Community

Vikash Kumar
Vikash Kumar

Posted on

Write your custom generic utility in TypeScript

I recently found myself writing the same kind of TypeScript type transformation in a few different places.

Hello,

I'm Vikash Kumar.

I had an existing type where most properties were required, but for one particular use case, I wanted some of them to become optional.

At first, I was just writing the transformation inline.

Then I realized:

Why not turn the pattern into a reusable utility?

So I built this:

export type Optional<T, K extends keyof T> =
  Omit<T, K> & Partial<Pick<T, K>>;
Enter fullscreen mode Exit fullscreen mode

And honestly, this tiny utility has been surprisingly useful.

The problem

Imagine we have a configuration type:

type Config = {
  name: string;
  version: string;
  description: string;
};
Enter fullscreen mode Exit fullscreen mode

Normally, TypeScript expects all three properties:

const config: Config = {
  name: "my-app",
  version: "1.0.0",
  description: "A demo app",
};
Enter fullscreen mode Exit fullscreen mode

But what if description is optional in a particular function?

We could create another type manually:

type PartialConfig = {
  name: string;
  version: string;
  description?: string;
};
Enter fullscreen mode Exit fullscreen mode

This works.

But now we have duplicated our type definition.

And duplication gets annoying when the original type becomes bigger.

Enter Optional<T, K>

With the utility:

type PartialConfig = Optional<Config, "description">;
Enter fullscreen mode Exit fullscreen mode

Now TypeScript understands:

const config: PartialConfig = {
  name: "my-app",
  version: "1.0.0",
};
Enter fullscreen mode Exit fullscreen mode

name and version remain required.

description becomes optional.

That's it.

No duplicate type.

No manual rewriting.

What's actually happening here?

The utility looks complicated at first:

Omit<T, K> & Partial<Pick<T, K>>
Enter fullscreen mode Exit fullscreen mode

But it's really just combining three TypeScript utilities.

Pick

Pick<T, K>
Enter fullscreen mode Exit fullscreen mode

Selects the properties we care about.

For example:

Pick<Config, "description">
Enter fullscreen mode Exit fullscreen mode

becomes:

{
  description: string;
}
Enter fullscreen mode Exit fullscreen mode

Partial

Partial<Pick<T, K>>
Enter fullscreen mode Exit fullscreen mode

makes those selected properties optional:

{
  description?: string;
}
Enter fullscreen mode Exit fullscreen mode

Omit

Omit<T, K>
Enter fullscreen mode Exit fullscreen mode

keeps everything except those properties:

{
  name: string;
  version: string;
}
Enter fullscreen mode Exit fullscreen mode

Finally, we combine them:

Omit<T, K> & Partial<Pick<T, K>>
Enter fullscreen mode Exit fullscreen mode

Which gives us:

{
  name: string;
  version: string;
  description?: string;
}
Enter fullscreen mode Exit fullscreen mode

Why this is useful?

This pattern becomes especially handy when working with APIs, configuration objects, constructors, and update functions.

For example, imagine an update function:

type User = {
  id: string;
  name: string;
  email: string;
  role: "admin" | "user";
};
Enter fullscreen mode Exit fullscreen mode

When updating a user, we probably don't want to require every property.

But perhaps id should still be required:

type UserUpdate = Optional<
  Pick<User, "id" | "name" | "email" | "role">,
  "name" | "email" | "role"
>;
Enter fullscreen mode Exit fullscreen mode

Now:

const update: UserUpdate = {
  id: "123",
};
Enter fullscreen mode Exit fullscreen mode

is perfectly valid.

And so is:

const update: UserUpdate = {
  id: "123",
  role: "admin",
};
Enter fullscreen mode Exit fullscreen mode

But this won't compile:

const update: UserUpdate = {
  role: "admin",
};
Enter fullscreen mode Exit fullscreen mode

Because id is still required.

That is where these small generic utilities start becoming really powerful.

The interesting part

What I like most about this isn't the utility itself.

It's realizing that TypeScript lets us build our own vocabulary for common type patterns.

Instead of repeatedly expressing:

Omit<T, K> & Partial<Pick<T, K>>
Enter fullscreen mode Exit fullscreen mode

I can simply say:

Optional<T, K>
Enter fullscreen mode Exit fullscreen mode

The type system becomes easier to read.

And once a pattern appears often enough in your codebase, turning it into a generic utility can make the whole project feel cleaner.


I used to think TypeScript utility types were mostly something I needed to memorize.

Lately, I've started thinking about them differently:

They're building blocks.

Once you understand Pick, Omit, Partial, intersections, and generics, you can start composing your own utilities for the problems your codebase actually has.

And that's probably the fun part. 😄

Have you built a small TypeScript utility recently that ended up becoming surprisingly useful?

Top comments (0)