TypeScript

Lesson 09

Utility types at API boundaries

Derive focused request and view types from domain types without exposing everything.

Good Code

review-api.ts
type PullRequest = {
  id: string;
  title: string;
  authorId: string;
  mergedAt: string | null;
  internalScore: number;
};

type PullRequestListItem = Pick<PullRequest, "id" | "title" | "authorId">;

function serializePullRequest(item: PullRequest): PullRequestListItem {
  // Pick exposes only the fields this boundary promises.
  return {
    id: item.id,
    title: item.title,
    authorId: item.authorId,
  };
}

Bad Code

review-api.ts
type PullRequest = {
  id: string;
  title: string;
  authorId: string;
  mergedAt: string | null;
  internalScore: number;
};

function serializePullRequest(item: PullRequest): PullRequest {
  // Returning the domain object leaks internal fields.
  return item;
}

Review Notes

What to review

Good Code

The good version exposes only the fields the list API needs while keeping the response type tied to the domain model.

Bad Code

The bad version returns the full domain object, including internal fields that a boundary should not promise to external callers.

Takeaways

  • Use Pick, Omit, Partial, and Readonly to express boundary-specific contracts from existing domain types.