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 RecordPartialRequiredPick and a custom Omittypes.

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:

Type Record is used for a dictionary of products in a user 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:

Webstorm autocomplete tool suggests to add name and amount for CartState variable

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):

Use Record for errors dictionary in business model

Let’s see how you can use it for type enhancing when working with Material-UIlibrary. 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 a theme as an argument and returns the future classNamewith correspondent styles and you want to define a type for this function:

Adding type for styles function in every component file

You notice that it can become very annoying to add these as CSSPropertiesfor every style object. Alternatively, you can use the benefit of the Record type and define a type for the styles function:

Using once defined createThemeFunction type everywhere

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:

On the left: using Partial type to reach the same result as on the right where every property is marked optional

Or you can use Partialto define some of the props as default props for your component:

Use Partials in typings for default props

As the opposite, the Required built-in type introduced in Typescript v2.8, makes all properties of a described object required:

All fields in OwnProps of this component are 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:

A small hack to ensure compiler that ticketOffer is required for the selector processing

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 Productproperties except of name and description:

Pick and Omit are flexible ways to re-use your interfaces

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:

Extending compact interfaces to reuse them for a different component typings

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.