TypeScript is a powerful tool for enhancing JavaScript with strong typing. But like any tool, it can be misused. A common anti-pattern in TypeScript is duplicating types instead of leveraging its utility types. This leads to bloated, harder-to-maintain code.
Let’s explore this with an example and how to improve it by "typing smartly."
The Problem: Duplicating Types
Consider this "bad" approach (as shown in the image):
type User = {
id: number;
name: string;
email: string;
};
type UserPreview = {
id: number;
name: string;
};
type UserEdit = {
id: number;
name: string;
isAdmin: boolean;
};
What's wrong here?
- Repetition: Fields like
id
andname
are repeated. - Scalability issues: If the
User
type changes, all dependent types must be updated manually. - Prone to bugs: Easy to forget updating one of the derived types.
The Solution: Utility Types to the Rescue
TypeScript provides built-in utility types that help us avoid redundancy and keep types consistent.
Here’s a better approach:
type User = {
id: number;
name: string;
email: string;
};
type UserPreview = Pick<User, "id" | "name">;
type UserEdit = Omit<User, "id" | "email"> & { isAdmin: boolean };
type UserOptional = Partial<User>;
Let’s break it down
Pick<Type, Keys>
Create a new type by selecting a subset of properties.
type UserPreview = Pick<User, "id" | "name">;
Omit<Type, Keys>
Create a new type by excluding certain properties.
type UserEdit = Omit<User, "id" | "email"> & { isAdmin: boolean };
Partial<Type>
Make all properties optional.
type UserOptional = Partial<User>;
Bonus Examples
Let’s look at a few more real-world patterns where this approach shines.
1. Product Management
type Product = {
id: string;
title: string;
description: string;
price: number;
createdAt: Date;
};
type ProductCard = Pick<Product, "id" | "title" | "price">;
type ProductUpdate = Partial<Omit<Product, "id" | "createdAt">>;
2. Blog System
type Blog = {
id: number;
title: string;
content: string;
author: string;
tags: string[];
};
type BlogSummary = Pick<Blog, "id" | "title" | "tags">;
type BlogForm = Partial<Omit<Blog, "id">>;
Clean Code Benefits
- 🔁 DRY (Don't Repeat Yourself)
- 📦 Reusable components
- 🔍 Easy Refactoring
- 🧪 Consistent Typing
- 🚀 Faster development
💡 Pro Tip: Combine Utility Types
You can also compose utility types for more control:
type EditableUser = Partial<Pick<User, "name" | "email">>;
This gives you a type where only name
and email
are optional — not the full User
.
🛠️ Custom Utility Types
If you find yourself repeating patterns, you can even write your own utility types:
type Without<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type CustomEditUser = Without<User, "email">;
🏁 Final Thoughts
Using TypeScript utility types like Pick
, Omit
, and Partial
isn’t just a trick — it’s a best practice. It helps you write concise, safe, and easily maintainable code.
Next time you feel like copying and tweaking a type, stop. Think: Can I simplify this with a utility type? Most likely, the answer is yes.
✨ Stay DRY, Stay Typed.
If you liked this post, share it with your TypeScript squad!