DEV Community

Cover image for Using `Pick<T>` for DTOs in TypeScript
Moorthy G
Moorthy G

Posted on

Using `Pick<T>` for DTOs in TypeScript

When working with DTOs in TypeScript, I often see (and use) Pick<T>.
It’s simple, expressive, and removes a lot of boilerplate — but it also has trade-offs that are easy to overlook.

Here’s how I think about it.


What Pick<T> Is Good At

Pick<T> creates a new type by selecting fields from an existing one.

type User = {
  id: string;
  email: string;
  passwordHash: string;
  createdAt: Date;
};

type UserDTO = Pick<User, "id" | "email">;
Enter fullscreen mode Exit fullscreen mode

✅ Less Duplication

I don’t have to redefine the same fields over and over.
The DTO stays automatically in sync with the domain model.

✅ Refactor-Friendly

If I rename email or change its type, TypeScript tells me immediately what breaks.
That’s a big win in real-world codebases.

✅ Great for Simple Read DTOs

For internal tools or basic CRUD APIs where the DTO closely mirrors the entity, Pick<T> works really well.


Where Pick<T> Starts to Hurt

❌ Tight Coupling to the Domain

DTOs are contracts. Pick<T> ties them directly to internal models.

type PublicUserDTO = Pick<User, "id" | "email">;
Enter fullscreen mode Exit fullscreen mode

Now a change in User can accidentally become a breaking API change — even if that wasn’t the intent.

❌ Semantic Mismatch

Sometimes the API shape isn’t the same as the domain shape.

  • Domain: firstName, lastName
  • API: fullName

Using Pick<T> hides the fact that transformation is required, which makes intent less clear.


How I Use It in Practice

I treat Pick<T> as a convenience, not a rule.

I’m happy to use it for:

  • Internal APIs
  • Read-only DTOs
  • Admin panels

For public or long-lived APIs, I prefer explicit DTOs:

type PublicUserDTO = {
  id: string;
  email: string;
};
Enter fullscreen mode Exit fullscreen mode

Sometimes I mix both approaches:

type UserBaseDTO = Pick<User, "id" | "email">;

type UserWithStatusDTO = UserBaseDTO & {
  status: "active" | "inactive";
};
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

Pick<T> is great when the domain and the DTO mean the same thing.
It’s risky when they don’t.

I use it where it reduces noise, and avoid it where clarity and boundaries matter more.

Type safety is great — but clear intent is better.

Top comments (0)