Advanced TypeScript Types with Examples
Improve your understanding of TypeScript and learn these advanced techniques to help you master the language and utilize TypeScript with React
When I began using TypeScript last winter, I have grown from defaulting to using any
for all types more complex than a string
or number
to now feeling comfortable with the usage of advanced built-in types and custom types. Add types to your JavaScript code by switching to TypeScript to write air-tight applications. This article will provide examples of using advanced types and also how to use them in a React application.
We will discuss the Record
, Partial
, Required
, Pick
and a custom Omit
types.
Record
A very useful built-in type introduced by Typescript 2.1 is Record
: it allows you to created a typed map
and is great for creating composite interfaces. To type a variable as Record
, you have to pass a string as a key and some type for its corresponding value. The simplest case is when you have a string
as a value:
const SERVICES: Record<string, string> = {
doorToDoor: "delivery at door",
airDelivery: "flying in",
specialDelivery: "special delivery",
inStore: "in-store pickup",
};
This may appear trivial, but it provides easy typing in your everyday code. One of the popular cases when Record
works in well is an interface for a business entity that you keep in a dictionary as key-value pairs. This model could represent a collection of contacts, events, user data, transportation requests, cinema tickets, and more. In the following example, we create a model for products that a user could add to her cart:
You see how the editor autocompletion will help us to define a typed object and will mark the variable with an error because not all the required properties are defined:
Also, Typescript does not allow us to create an empty object for some defined shape and then populate it with properties, but here Record
will come to the rescue.
It is also possible to use a string
enum
as a key in the Record
type. For example, we will use ErrorsEnum
to keep and access possible error values (messages):
Let’s see how you can use it for type enhancing when working with Material-UI library. As the guide says, you can add your custom styles with CSS-in-JS notation and inject them via
withStyles
HOC. You can define styles as a function that takes atheme
as an argument and returns the futureclassName
with correspondent styles and you want to define a type for this function:
You notice that it can become very annoying to add these as CSSProperties
for every style object. Alternatively, you can use the benefit of the Record
type and define a type for the styles
function:
Now you can use it safely in every component and get rid of hardcoding type of CSS properties in your styles.
Partial and Required
Partial
type makes all properties in the object optional. It could help you in many cases, like when you’re working with the data that a component would render but you know that the data may not be fetched at the moment of mounting:
Or you can use Partial
to define some of the props as default props for your component:
As the opposite, the Required
built-in type introduced in Typescript v2.8, makes all properties of a described object required:
One of the use cases for Required is selectors: sometimes you want to make a selector for a property from a nested object and you know that at the moment of selector invocation this property will be defined. You may point it out with a typing:
This may look like a cheat and it can cause type errors if you start to inherit required properties from optional ones, so be careful!
Maybe it sounds stupid but it’s not a rare situation when you have code generated automatically and all interfaces that are in your hands are Partial and all elements of your UI want only Required. Here you’ll start to check every nested object on undefined 😨.
Pick and Omit
Have you ever tried to narrow a type because you realized that your next class doesn’t need this bunch of properties? Or maybe you arrived at this point in the process of refactoring, trying to distribute pieces of a system in a new way. There are several types that can solve this problem.
Pick
helps you to use a defined already interface but take from the object only those keys that you need.
Omit
which isn’t predefined in the Typescript lib.d.ts
but is easy to define with the Pick
and Exclude
. It excludes the properties that you don’t want to take from an interface.
At both of these images, ProductPhotoProps
will contain all Product
properties except of name and description:
One of a practical example of such a situation from my current project is a refactoring a large form with a complex fields dependencies. There was
FormProps
type where errors field was included. After re-thinking this architecture the errors became unnecessary for a first child component but still needed by the second one. I used Pick to take a portion of fields except for errors for a new interface and it worked well.
There are, of course, different ways to combine types and define their relationship. If you start to decompose a large thing into small pieces from the beginning, maybe you will solve the problem of excluding properties from an object from another side. You would instead extend types.
Extending type/interface
When you extend an interface, all properties described in a source interface/type will be available in a result interface. Let’s see how we could combine small interfaces into one that corresponds to our task:
This method is not as handy because you have to imagine the shape of your objects in advance. On the other hand, it is fast and easy which makes it cool for prototyping or building simple UI like rendering data into read-only blocks.
Summary
We have explored some of the popular pre-defined TypeScript types with the real code examples. The project we used is only a demo but all of these types work at least in one of the real-world apps that I know 😉. I hope this article was useful for you and encouraged you to not be afraid of writing typings. I created a repo with a React-Redux SPA where you find most of these examples or their alternatives.
However, I would like to say one more thing about static typing. Often, when you explore a new technology or face a challenge during a feature development, you start to solve a technical problem and may forget about your goal. Static typing is not a goal of your work, it’s just a tool. If it becomes a central thing of a project, it’s a sign that you got carried away 🚀. Remember about balance in business/tech parts of your app and happy coding!