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.