published on

3 Nifty Typescript Utility Types to Embrace

Oftentimes I find myself struggling with Typescript code style and readability due to complicated types. In this article we are going to take a look at a couple of utility types provided natively by TypeScript that might help you too to clean up your type systems for a leaner and meaner codebase.

1. Using the Partial utility type

TypeScript has a bunch of utility types that help you with cleaning up your code and making your shapes scale better. We will go through a couple of these utility types and we will start with Partial.

Partial describes a part of a type, or in other words, creates a type that sets all properties of the original type as optional. This can be super useful when you’re trying to helpers to dynamically access values.

type Filters = {
    name: string;
    age: number;
    occupation: string;
}

function updateFilters(filters: Filters, newFilters: Partial<Filters>) {
    return {...filters, ...newFilters};
}

const filters: Filters = {
    name: '',
    age: 42,
    occupation: 'programmer'
}

const newFilters = updateFilters(filters, {name: 'Akos'});

console.log(filters, newFilters);

/*
{
  "name": "",
  "age": 42,
  "occupation": "programmer"
},  {
  "name": "Akos",
  "age": 42,
  "occupation": "programmer"
} 
*/

The above example describes a use-case where we have a filtering system implemented on our frontend. Let’s say we have a list of people there with a name, an age and an occupation and we would like to describe our active filters with a nice shape describing these three. As you can see, we utilised Partial in our helper function to create a new filter to update the filters to also include the name “Akos.” With this simple tool, we can describe a very flexible and error resistant way of changing our data.

Partial is very useful for cases where we need flexibility. However, in some cases, we want to make sure that certain properties just aren’t accessible to some parts of our application. Let’s take a look at another utility type to solve this problem for us.

2. Using the Omit utility type

We have seen the use of a nice and convenient utility type, let’s take a look at another one in the form of Omit. Just above we talked about Partial. It was a fantastic tool to make all properties of a type optional, however, in some cases we just need to be more strict with what shapes can enter what part of our systems. Let’s take a look at the following example:

type User = {
    oid: string;
    firstName: string;
    lastName: string;
    email: string;
}

function GDPRSensitiveMethod(user: User) {
    /// try to access params of user and read them and do nasty.
}

In the above code we can see that we have a User type that is used in a function that might do some tinkering with this sensitive data, potentially storing it in systems that are not fully compatible to GDPR just yet. We want to make sure that in this case we protect the data of our users (even from ourselves) and we can use the Omit utility type to drop GDPR sensitive fields before putting it into this function.

type GDPRSensitiveUser = Omit<User, "firstName" | "lastName" | "email">;

function GDPRSensitiveMethod(user: GDPRSensitiveUser) {
    /// try to access params of user and read them and do nasty.
}

With the code above, we ensure that only the oid field is passed on from the original User type, which is not GDPR sensitive, so we can do whatever we want with this input data at this point in our function.

Restricting access to members can sometimes be enough to avoid catastrophes. However, in some cases, there is another utility type that might ensure less bugs in your codebase for mutability-sensitive parts of your application

3. Using the Readonly utility type

With the recent rise of functional programming and with it immutable programming patterns, there has been more and more demand for our code not to modify values of our variables. With the rise of ES6, the const keyword provided by JavaScript fixed a lot of our issues, however, it didn’t quite give the immutability that we might expect. Consider the following snippet:

type Animal = {
    legs: number;
    sound: string;
}

const dog: Animal = {
    legs: 4,
    sound: "woof",
}
console.log(dog.sound)
dog.sound = "vau";
console.log(dog.sound)

In the above code, we have defined an Animal type and an instance of it called dog we might think that the code would not work, since dog was instantiated with the const keyword, however, in JS the const keyword means that the variable itself cannot be assigned new content, however, if the value is an object, the value of the object can still be altered. Resulting in the following output:

[LOG]: "woof" 
[LOG]: "vau" 

Before we plunge into unsolvable drama here, luckily we have a tool provided by TypeScript natively to ensure that instances of our types cannot be altered, this is called the Readonly utility type. Let’s see it in action:

type Animal = {
    legs: number;
    sound: string;
}

type EndangeredAnimal = Readonly<Animal>;

const dog: EndangeredAnimal = {
    legs: 4,
    sound: "woof",
}

console.log(dog.sound)
dog.sound = "vau";
console.log(dog.sound)

In the above example, the program fails with a compilation error:

Cannot assign to ‘sound’ because it is a read-only property.

This means, that with this utility type, you can ensure immutability for your sensitive variables in your system, resulting in less errors on the long run. Thanks, TypeScript!

Summary

We have taken a look at a good number of tools today. Here’s a quick summary:

Partial is a utility type that creates a new type with all properties of the input type set as optional. Can be useful when building flexible data update tools for your types.

Omit is also a utility type that create a new type with all the specified properties removed from the original type. This can be useful when you want to create stricter types from already existing ones for parts of your systems.

Readonly is yet another utility type that enables you to make all the instances of your type to prohibit value re-assignment to all of their properties.

Thanks everyone for reading so far, looking forward for our next session!