5 utility types in Typescript and their use cases.

5 utility types in Typescript and their use cases.

The typescript language provides, right out-of-the-box, several utility types that facilitate type creation. These utilities make it possible to generate new type definitions from existing ones without manually typing them ( new type definitions ) out . They sort of recycle existing types to produce new ones .

They are globally available and don't need to be imported.

Here, I wrote on five of these utility types and gave a use case for each.

ReturnType

This utility type generates type definition from function return values. It takes a function and returns the return type of the function .


// 1
type Age = ReturnType<() => number>

// variable age takes a number
const age: Age = 67


// 2
function getStudent() {
   return {
      name: "John Doe",
      age: 56,
      hobby: "playing"  
   }
}

type Student = ReturnType<typeof getStudent>

const studentA: Student = {
   name: "David Giggs",
   age: 45,
   hobby: "sleeping"
}

Notice that when there is a function declaration , what’s passed to ReturnType istypeof <functionName>.( typeof getStudent in above snippet )

  • Use case

    A valid use case is when there is a need to type a variable based off what’s returned by a function imported from a library. An example can be seen when setting up Redux-ToolKit Query to use in React.

    N.B: You don’t need to know or learn Redux to get this! Not going into the nitty-gritty of that , trust me ;)

import { configureStore } from '@reduxjs/toolkit'
// ...
const store = configureStore({
  reducer: {
    one: oneSlice.reducer,
    two: twoSlice.reducer,
  },
})
export type RootState = ReturnType<typeof store.getState>

export default store

The configureStore ( in snippet above ) on accepting a reducer function as a named argument generates a store object which contains the getState method ( function within an object ) that returns a state object.

A need to use the type of this state object elsewhere in the app required the export const type RootState = … line where ReturnType is used in getting the return type of the getState method.

Pick

This constructs a subset of an existing type. Just as its name, it enables one to explicitly select the particular type(s) that is(are) needed while omitting others.

Below, there is a Student type but the StudentCard type requires the name and age properties only. So we proceed to “pick” these properties rather than manually creating StudentCard's type.


type Student {
        id: number,
        name: string,
        age: number,
        gender: string
}

type StudentCard = Pick<Student, "name" | "age">

const studentCard: StudentCard = {
   name: "John",
   age: 46
}
  • Use case

    A nice use case is when there is a need to create a sort of preview of an object and just few of its properties are required in achieving that.
type Car = {
     id: number;
   name: string;
   color: string;
   model: number;
   wheels: number;
   speed: number;
   weight: number;
   price: number
}

type CarPreview = Pick<Car, "name" | "color">

const carCard: CarPreview = {
   name: "Audi",
   color: "black"
}

// displayCarCard is assumed to "display"( return ) a car card which shows only the 
// car name and color.
// But it requires the card object as a parameter to know what data to display
function displayCarCard(card: CarPreview) {
   return card
}

Omit

The Omit utility function works exactly as Pick but it drops the listed properties . Indirectly , it picks properties that are not listed out as opposed to Pick. Using same example as Pick's:

type Student {
        id: number,
        name: string,
        age: number,
        gender: string
}

type StudentCard = Omit<Student, "name" | "age">

const studentCard: StudentCard = {
   id: 1729,
   gender: "male"
}

Use case

Omit finds application in situations where Pick does. One is when the number of properties to pick are many and the use of Pick can be ineffective . For instance, a scenario where a car profile needs to be displayed and the id is not required. Listing out what you want won’t look nice when you can just list out what you don’t need ( id ) since they are not many

type Car = {
  id: number;
  name: string;
  color: string;
  model: number;
  wheels: number;
  speed: number;
  weight: number;
  price: number;
};

type CarProfile = Omit<Car, "id">;

const carProfile: CarProfile = {
   name: "Fiat",
   color: "black",
   model: 500,
   price: 4500,
   speed: 670,
   weight: 850,
   wheels: 4
};

// displayCarProfile is assumed to "display"( return ) a car profile that shows all
// car properties except for id.

function displayCarProfile(profile: CarProfile) {
  return profile
}

With Omit , a long list of properties that would have been needed when using Pick was avoided. Imagine using this : Pick<Car, "name" | "color" | "model" | "price" | "speed" | "weight" | "wheels"> when Omit<Car, "id"> gives same result.

Partial

This utility type is designed to make all of a type’s properties optional . It is worthwhile to note that with partial alone, there is no way to choose which properties are to remain optional or required i.e. all properties will be made optional.

Consequently, the student object in example below could be an empty object, one with just an id property or just a name property only, or one with both an id and name properties.

type Student {
        id: number,
        name: string,
        age: number,
        gender: string
}

type OptionalStudent = Partial<Student>
// what OptionalStudent is ?
// type OptionalStudent = {
//        id?: number,
//        name?: string,
//        age?: number,
//        gender?: string
// }

const studentA: OptionalStudent = {}

const studentB: OptionalStudent = {
   id: 1729
}

const studentC: OptionalStudent = {
   id: 1729
   name: "John Doe"
}

A combination of the Partial utility and the Omit/Pick utility makes it possible not just to select properties but also specify which properties are optional or required.

For instance , if a type that contains a name and an optional age property is to be constructed from the type Student.

type Student {
        id: number,
        name: string,
        age: number,
        gender: string
}

type StudentWithOptionalAge = Partial<Pick<Student, "age" | "name">> & Pick<Student, "name" >

const studentD: StudentWithOptionalAge = {
    name: "John"
}

const studentE: StudentWithOptionalAge = {
   name: "Sarah",
   age: 4
}
  1. Pick<Student, "age" | "name"> generates a type consisting of only the age and name property
  2. Partial<Pick<Student, "age" | "name">> takes type generated in 1 and constructs a type whose properties ( all ) are optional.
  3. Pick<Student, "name"> generates a type consisting of only the name property*
  4. An intersection type ( StudentWithOptionalAge ) , where age property is optional while name property is required, is then generated from results of 2 & 3.

💡 An intersection type combines multiple types into one.

💡 For more on combining typescript utilities types: daily-dev-tips.com/posts/combining-typescri..

Record

Unlike the previous utilities, Record takes the form Record<Key, Type>, where

  • Key is the type of the key, and

  • Type is the type of the keys' values

The record utility type creates an object type that has properties of a particular type ( Key ) with corresponding values of another( or same type as Key) type ( Type ).


interface DogInfo {
  age: number;
  breed: string;
}

type DogName = "sky" | "apollo" | "billy";

const dogs: Record<DogName, DogInfo> = {
  sky: { age: 8, breed: "Akita" },
  apollo: { age: 6, breed: "Bullmastiff" },
  billy: { age: 10, breed: "Bulldog" },
};

Use case

Record is useful when all items have a similar type of value, especially when there is a large number of them :


type Students = "John" | "Khalid" | "Jane"

type StudentInfo = {
   gender: "male" | "female"
   age: number
}

const students: Record<Students,StudentInfo> = {
   Jane: {
      age: 56,
      gender: "female"
   },

   John: {
      age: 22,
      gender: "male"
   },
   Khalid: {
      age: 5,
      gender: "male"
   }
}

Wrap Up

There are more utility types provided by typescript but I have only highlighted five of them in this article. Tried any of typescript's utility types before ? I'll be glad to know how you used them in the comments section below.

Feel free to connect with me on LinkedIn and Twitter.

See you in my next article 👋