-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
185: Pick<Type, Keys> and Omit<Type, Keys> Revisited
I once worked with a developer—let's call him Marcus—who was managing a massive User interface with about 30 different fields. He needed a separate type for the API response that sent user data to the frontend, so he just copied and pasted the User interface into a new one called UserResponse and manually deleted the passwordHash and internalAdminNotes fields. It seemed fine for a week. Then, we added a timezone field to the main User model. Marcus forgot to update UserResponse, and suddenly the frontend was crashing because it expected a timezone that simply wasn't being passed along. This is the "manual synchronization trap," and it's exactly why we use Pick and Omit.
Stripping Sensitive Data with Omit
When you have a "source of truth" type—like a database model—you rarely want to pass that entire object around your application. Most of the time, you want a version of that type that is almost the same, but missing a few sensitive or irrelevant fields. That's where Omit<Type, Keys> shines.
interface UserAccount {
id: string;
username: string;
email: string;
passwordHash: string;
lastLoginIp: string;
createdAt: Date;
}
// Instead of redefining the whole thing, we just say:
// "Give me everything in UserAccount EXCEPT these two."
type UserProfile = Omit<UserAccount, 'passwordHash' | 'lastLoginIp'>;
const publicProfile: UserProfile = {
id: 'usr_123',
username: 'dev_pro',
email: 'hello@example.com',
createdAt: new Date(),
// passwordHash: '...' // This would now trigger a TypeScript error
};
The beauty here is that if I add preferredLanguage to UserAccount tomorrow, UserProfile automatically inherits it. I don't have to hunt through my codebase to update five different "User-like" interfaces.
Creating Lean Views with Pick
While Omit is great for removing a few things, it's often the wrong tool if you only need a tiny fraction of a large object. If your interface has 20 fields and you only need 2, omitting 18 fields is tedious and makes your code harder to read. In those cases, I always reach for Pick<Type, Keys>.
Imagine you're building a user directory. You don't need the full UserProfile for a list of 100 users; you just need their names and avatars. Using Pick allows you to be explicit about what your component actually depends on.
interface UserAccount {
id: string;
username: string;
email: string;
passwordHash: string;
avatarUrl: string;
bio: string;
location: string;
}
// We only want a small slice of the data for the directory list
type UserDirectoryItem = Pick<UserAccount, 'username' | 'avatarUrl'>;
function UserAvatar({ user }: { user: UserDirectoryItem }) {
return `<img src="${user.avatarUrl}" alt="${user.username}" />`;
}
I personally find that Pick is a great way to document your intentions. When another engineer looks at UserDirectoryItem, they immediately know exactly which pieces of the user object are being used in that specific context, without having to guess what was "left over" from an Omit.
📋 Practical Task
Refactoring the Product Management API
You are working on an e-commerce backend. You have a central Product interface, but you've noticed that the team is manually creating duplicate interfaces for different API endpoints, leading to bugs. Your task is to refactor these using Pick and Omit.
interface Product {
id: string;
sku: string;
name: string;
description: string;
price: number;
costPrice: number; // Sensitive: should not be sent to customers
stockQuantity: number;
category: string;
imageUrl: string;
createdAt: Date;
updatedAt: Date;
}
// 1. Create a type 'ProductPublicView' that includes everything
// EXCEPT 'costPrice' and 'stockQuantity'.
type ProductPublicView = // Your code here
// 2. Create a type 'ProductThumbnail' that ONLY includes
// 'id', 'name', and 'imageUrl'.
type ProductThumbnail = // Your code here
// 3. Create a type 'ProductUpdateInput' that allows updating
// anything EXCEPT the 'id', 'sku', and 'createdAt' fields.
type ProductUpdateInput = // Your code here
There are no comments for now.