TypeScript has some incredibly nifty utilities that can be used to make your codebase more readable, efficient and safer.
In this article, I've compiled a list of four of my favourite utilities that I use in my daily workflow, along with some examples and explanations of each.
They've helped my TypeScript workflow - I hope they help yours, too!
If you're new to TypeScript, I have a full course for beginners available right here on my YouTube channel!
If that sounds like something you're looking for, check it out here - I'd love to hear your thoughts on it!
Omit are special utility types that TypeScript provides as a way to add more convenience and power when creating new types for object shapes. Let's take a look at each one in detail with some examples...
In the following example, we've constructed an
interface type called
Consumable, which has got a bunch of properties that relate to something you could eat or drink.
TypeScript provides the
Pick utility to allow us to "pluck" properties from our object shape types, and create a new type from that. Let's create a new type,
Pizza, by simply picking out the relevant properties from the
Nice! Let's go over that in a little more detail.
- The first parameter that we pass into the
Pickutility is the type that we want to pick from.
- The second parameter is either a single value or a union type of all of the properties we want to pick out from the type we passed in as the first parameter.
In the above example, we're picking
caloriesPerServing from the
Consumable type to construct our brand new type,
Let's go one step further. The cool thing about creating a new type is that we can use it just like anything else - so let's extrapolate our
Pizza type and add a
toppings property to our object shape...
In this example, we're declaring
Pizza as an
interface, so that we can extend from our new
Picked type and add a brand new parameter,
toppings, to it. That means that our
Pizza interface, after being compiled, would have the following properties:
- size: 'large' | 'medium' | 'small'
- caloriesPerServing: number
- toppings: string
Omit works just like
Pick - but the inverse.
Pick the properties we wish to pluck out from the object type, but with
Omit, we pass the properties we wish to exclude from the initial object type.
Let's take a look at an example to make things a little clearer. Just like with
Pick, we'll use the same
Consumable type once again as a base - but this time, we'll create a new type called
Consumable type has a property on it called
millilitresPerServing. That's not really relevant to a sandwich - so by using
Omit, we can pass in two arguments:
- First, the type that we wish to use as a base...
- ...followed by a single or union type of the keys that we wish to omit from that interface.
(Just like with
That means in this example, our
Sandwich type would have the following properties:
- size: 'large' | 'medium' | 'small'
- caloriesPerServing: number
- gramsPerServing: number
millilitresPerServing isn't present in that list - that's because our
Sandwich type intentionally omits that from our new type by using the
Omit utility as described above.
What's just as cool - just like with
Pick, the previous example, we can use the new type generated by the
Omit utility as a base to extend from. Let's extend our
Sandwich type by adding some
Omit and Pick really come into their own in more complex applications, particularly when you have a lot of overlapping object shapes that have properties which should remain identical in type. They're a dream for composition!
Omit that we covered above,
Partial are utility types that allow us to create new types from our object types. Let's take a look into each one to see how they could be used as part of a workflow.
Okay, simple example - we have an interface for a (fictional) sign-up form on a website, with all the usual suspects present.
Notice that in the above example, we've got a few
?s in there.
Those are use to indicate that those properties are optional - which means that they're allowed to be
undefined. Let's create an input object using our type:
(Note: I could have also just omitted all of of the properties with
undefined as a value, but I wanted this example to be a bit more explicit for easy reading!)
Let's say for example that we have another form in our web app elsewhere, which uses the same shape of input - but this time, requires that we supply values to all of the properties in our
If we wanted to, we could just re-write that same interface again, keeping all our keys and value types the same - but removing those pesky
?s to ensure that we can't pass any
undefined values in...
...but, following the classic DRY rule, this should start to leave a bit of a bad taste in your mouth. There must be a better way...
Thankfully, that's where the wonderful
Required utility comes in!
Let's create a new type called
MyFormInputsRequired and make all of the properties on it non-nullable.
Required simply takes one parameter - the interface or object type that we want to make all properties enforced. In the above example, we also create a new object using that interface, and ensure that every single property has a corresponding value.
If the key wasn't present in
requiredInputs, or if we supplied
undefined as any of the values, this would throw an exception at compile-time.
Nice and safe!
Partial is the exact opposite of
Required - instead of making all the properties in an interface or object type required, it makes them all optional. (if you've read this entire article from the top, you're probably beginning to notice a pattern...)
Let's take a look at an example on how it could be used. We'll go back to videogames to maintain some semblance of variation...
In the above example, we've introduced our
VideoGame interface, which has three properties on it which are all required.
Let's say we wanted to create a new type making all of the properties optional. We'll use the power of
Partial to make this happen...
In the example above, we create a new type named
VideoGamePartial, and, just like how we used
Required above, we pass the
Partial utility a single object type.
This creates a new type, copying the exact shape of the
VideoGame interface, but making all of the properties optional.
When we create a new object using our new
VideoGamePartial type (as demonstrated in the
nintendoGame value at the bottom of the above example), we can see that we're able to skip two of the previously required values -
Taking this to an extreme, because
Partial makes all of our properties optional, it would actually be valid to use that type to simply create an empty object...
...but that's probably more of a hypothetical use-case, as I can't imagine that being super useful in day-to-day 😅
Finally, topping it all off (and attempting to drive home how cool these utilities are) - let's use our new
Partial type as a base to extend from!
In the above example, we create a new type called
SonyVideoGame, which extends from our
VideoGame type that has a set of properties which are all optional.
We've then added a new (required!) type to it called
platform. That means that all of the properties (and their respective optional states would be as follows):
- title: string - Optional
- description: string - Optional
- ageRating: '3+' | '10+' | '16+' - Optional
- platform: 'PS2' | 'PS3' | 'PS4' | 'PS5' - Required
Using composition and the power of TypeScript utilities, we've created a complex type which has a series of properties which are both optional & required. Neat, right?
And that concludes our whistle-stop tour on some of TypeScript's powerful utilities that are provided with the language. There's plenty of others that you can delve into over at the TypeScript handbook - but these four are some of my favourites.
If you're looking for more TypeScript learnings, I have a full video course on the basics of TypeScript over on my website at CodeSnap.io!