Typescript: How to use utility types to keep your code DRY.

Larry Smithwick • July 1, 2020
​

Typescript: How to use utility types to keep your code DRY.

Types... If you are coming from a vanilla Javascript background you might see types as idealistic in theory but in practice tanks your productivity. Who wants to write a type for every possible shape some data could have anyways? In web development, we often get some chunk of data from an API clean it, process it, take off a few props here and add a few there and by the time you are done with all that you could end up with a ton of types that are all just variations of the same object. And honestly, writing multiple types for that same piece of data can be a nightmare to maintain.

​

Every line of code we write is another line of code we will have to read and maintain later. If I decide to change something fundamental I'll probably have to change every interface I have of that object as well. It may not seem like much overhead in the beginning but any experienced developer knows that projects only tend to get more complex over time and technical debt piles up quickly.

​ ​

If only we had some collection of utility types that allowed us to represent states of any type we create without having to write and maintain them ourselves...

Que in, Typescript utility types: Utility Types Documentation

There are quite a few of these (16 as of writing) and I'm not going to cover them all. I will cover some of the ones I found most useful and give some practical examples of how you can use them yourself.

1# Partial<T>

Constructs a type with all properties of T set to optional. This utility will return a type that represents all subsets of a given type. Let's say you have an interface for a client that you get from a backend API. The client needs to be able to change pieces of their profile and update them. There are many ways to do this but often the best solution is sending only the part of the client that changed, rather than reconstruct the entire object or create an endpoint for each field.

Doing this in Typescript you have a few choices. You could create an object of type any and lose all type safety and IntelliSense... not great. You could make another model with all of the fields being optional. This means that every time you make a change you now have to update two models. A Partial allows us to create a subtype of the client with all props optional on the fly for the specific context we are in, without any additional code.

 public class Client { 
    firstName: string; 
    lastName: string; 
    phone: number; 
    address1: string; 
    address2: string; 
  
    // ...ect 
  } 
  
  async updateClient(clientId: string) { 
    // create a "patch" of the object with only the fields you need to update. 
    const patch: Partial = { firstName: 'Larry', lastName: 'Smithwick' }; 
  
    // Some service that handles the http calls to the API. 
    const result = await this.clientService.updateClient(clientId, patch); 
  } 
 

This is great as we no longer have to maintain a second model for what is often a very limited scope. If either prop used is changed IntelliSense will still step and let us know that something is up since we still get to keep type safety.

2# Pick<T,U>

Sometimes a type can be very large and it can be difficult and cumbersome to find the property we need. Often it is best to limit the scope of data passed to our components to hide unnecessary details and prevent mistakes. Pick allows us to do just in a type-safe manner by removing only the properties we need into a new type.

 interface Account { 
    id: string; 
    client: Client; 
    blogs: Blog[]; 
    settings: AccountSettings; 
  
    // potentially MANY more properties. 
  } 
  
  type BlogsAndSettings = Pick ; 
  
  const blogs: BlogsAndSettings = { 
    blogs: account.blogs, 
    settings: account.settings, 
  }; 
 

Using intersection types we can even take only the properties we need from multiple types.

 interface Client { 
    id: string; 
    firstName: string; 
    lastName: string; 
    address1: string; 
    address2: string; 
  } 
  
  interface Blog { 
    id: string; 
    content: string; 
    title: string; 
    likes: number; 
  } 
  
  // "Client & Blog" creates a type with all properties of both. We then pass a string for each property we want.  
  // Even though we are using strings IntelliSense is still able to check our use of property names. 
  
  type BlogPreview = Pick ; 
  
  const blogPreview: BlogPreview = { 
    title: 'Utilizing Typescript Utility Types', 
    firstName: 'Larry', 
    lastName: 'Smithwick', 
  }; 
 

3# Omit<T,K>

Now let's say we are in a part of the application that deals with sensitive info. We need to send some of this info somewhere else to be displayed but want to make sure parts of it are left out. We could create another model with only the allowed properties OR we could just omit the ones we don't want.

 interface Client { 
    id: string; 
    firstName: string; 
    lastName: string; 
    address1: string; 
    address2: string; 
    socialSecurity: number; 
    creditCards: CreditCard[]; 
  } 
  
  interface Blog { 
    id: string; 
    content: string; 
    title: string; 
    likes: number; 
  } 
  
  type BlogPreview = Omit ; 
  
  const blogPreview: BlogPreview = { 
    title: 'Utilizing Typescript Utility Types', 
    firstName: 'Larry', 
    lastName: 'Smithwick', 
    creditCards: [], // We get an error on this line since creditCard is not a prop of BlogPreview. 
  }; 
 

4# Required<T>

Sometimes we need to fill out a model that has multiple optional types that need to be required for specific functions. Required is the opposite of Partial in that it takes every property of T and makes it required.

 interface Client { 
    firstName: string; 
    lastName: string; 
    phone?: number; 
  } 
  
  function createAccount(client: Required ) { 
    // creates account 
  } 
  
  const client1: Client = { 
    firstName: 'Larry', 
    lastName: 'Smithwick', 
  }; 
  
  // Error types of property 'phone' are incompatible. 
  createAccount(client); 
  
  const client2: Required = { 
      firstName: 'Larry', 
      lastName: 'Smithwick', 
      phone: 9078675309 
  } 
  
  // Accepts parameters without error. 
  createAccount(client2); 
 
 

Utility types are a great option to consider whenever your about to create a similar type of something you already have and helps save us from resorting to using the "any" type in our code. In fact, between utility types, intersections, and unions you will almost never need it. These tools will help keep your code DRY and type-safe at the same time.

​

Good luck! And remember, (type) safety first!

By VectorOne April 2, 2025
For many businesses, spreadsheets are the go-to solution for organizing data, managing workflows, and tracking progress. However, as companies grow, spreadsheets can become cumbersome, prone to errors, and inefficient. Manual data entry, version control issues, and lack of real-time collaboration can slow down operations and lead to costly mistakes. The solution? Transitioning from spreadsheets to digital automation.
By VectorOne March 4, 2025
We recently had some conversations with people in different levels about software changes in their organization. What we heard was...where do we start this conversation? As a business leader, you recognize that technology plays a vital role in efficiency, customer satisfaction, and long-term growth. However, convincing decision-makers to invest in new or upgraded software can be challenging. To get the conversation started, here are some key discussion points that highlight the need for change and the benefits of a tailored solution.
By VectorOne September 5, 2024
In today’s fast-paced business world, efficiency is key. Companies rely on a variety of systems to manage different aspects of their operations, from customer relationship management (CRM) tools to accounting software, inventory management, and more. However, a common challenge many organizations face is ensuring these systems communicate effectively with one another. When systems operate in silos, it can lead to inefficiencies, such as double data entry, errors, and lost productivity. This is where the importance of finding the right tech solutions for seamless system communication comes into play.
By VectorOne July 25, 2024
In the ever-evolving landscape of business and technology, the roles of professionals often blur and intertwine, leading to innovative approaches in project management. One such emerging trend is the shift from traditional project managers to designers taking the lead in projects. This shift offers a multitude of positive impacts, transforming the way projects are conceptualized, developed, and executed.
By VectorOne July 8, 2024
In the ever-evolving landscape of business, technology plays a pivotal role in shaping the success and efficiency of companies. However, with rapid technological advancements come significant challenges that companies must navigate. Here are the five biggest technology challenges companies face today and how partnering with a software development company can provide much-needed solutions.
By VectorOne June 10, 2024
Customer Relationship Management (CRM) systems have become essential tools for businesses of all sizes, streamlining operations and enhancing customer interactions. Adopting a CRM can offer numerous advantages, from improving customer service to driving sales and fostering collaboration within your team. Let's delve into the pros of using a CRM and explore how to choose the right one for your business needs. We'll also discuss the value of custom solutions when off-the-shelf options fall short.
By VectorOne May 31, 2024
In today's fast-paced digital world, software applications are at the heart of virtually every business operation and personal activity. From managing finances to communicating with teams across the globe, the importance of reliable and efficient software cannot be overstated. However, a significant challenge that persists across the software landscape is the lack of solid user interfaces (UI). This issue is more than just a minor inconvenience; it can lead to decreased productivity, frustrated users, and ultimately, the failure of otherwise promising applications.
Show More