TypeScript: Tailor required and optional fields for related API endpoints

TL;DR: TypeScript's utility types make it easy to tailor which model fields are required, optional, or even present across API endpoints.

When you're working with a REST API in TypeScript, it's a good idea to create interfaces for the models (or messages) you send and receive to take advantage of type checking and autocomplete.

Consider the model for a POST to /api/customers/.

interface Customer {
  displayName: string;
  email: string;
  bio?: string;
}

However, the model for a PUT to /api/customers/ looks different.

interface CustomerForUpdate {
  id: string;            // new field
  displayName?: string;  // optional for this verb
  email?: string;        // optional for this verb
  bio?: string;
}

You can create a different interface for each endpoint with varying field specifications, but this is one place where code duplication is bad and should be avoided.

Enter TypeScript's utility types. The Required, Partial, Pick, and Omit classes allow you to create one interface for the GET verb (which usually represents the complete set of required and optional fields) and a set of derived classes for other verbs and endpoints.

interface Customer {
  id: string;
  displayName: string;
  email: string;
  bio?: string;
}

interface CreateCustomer
  extends Pick<Customer, "displayName" | "email" | "bio"> {}
// Same as -->
// interface CreateCustomer {
//   displayName: string;
//   email: string;
//   bio?: string;
// }


interface UpdateCustomer
  extends Pick<Customer, "id">, Omit<Partial<Customer>, "id"> {}
// Same as -->
// interface UpdateCustomer {
//   id: string;
//   displayName?: string;
//   email?: string;
//   bio?: string;
// }

Using these utility classes are a more efficient way to take advantage of TypeScript's type checking and autocompletion while avoiding unnecessary duplication.