TypeScript Utility Type

TypeScript Utility Type

·

7 min read

TypeScript provides various utility types that make types transformation very easy. We can transform existing types into several other types.

These utility types are globally available, so we don't need to import them explicitly.

By using these utility types, we can write concise and maintainable code.

Let's understand how to use them with examples. Some of the most commonly used utility types are:

Partial<Type>

It converts all properties of a type to be optional. And it returns a type that represents all subsets of the given type.

interface User {
  name: string;
  gender:string;
  isAdmin:boolean;  
}

// Convert all properties of the User type to be optional
type PartialUser = Partial<User>;
  /* type PartialUser = {
        name?: string;
        gender?: string;
        isAdmin?: boolean;
    */}

// Create an instance of PartialUser
const user: PartialUser = {
  name: "Joker",
  isAdmin : false
};

Required<Type>

It makes all properties of the given type required. It is the opposite of Partial<T> utility.

interface User {
  name: string;
  gender?:string;
  isAdmin?:boolean;  
}
// Convert all properties of the User type to be required
type RequiredUser = Required<User>;
  /* type RequiredlUser = {
        name: string;
        gender: string;
        isAdmin: boolean;
    */}

// Create an instance of PartialUser
const user: RequiredUser = {
  name: "Joker",
  gender: "male"type A = Awaited<Promise<string>>;

type B = Awaited<Promise<Promise<number>>>;

type C = Awaited<boolean | Promise<number>>;

interface User {
  name: string;
  gender:string;
  isAdmin:boolean;  
}

// Convert all properties of the User type to be optional
type PartialUser = Partial<User>;type A = Awaited<Promise<string>>;

type B = Awaited<Promise<Promise<number>>>;

type C = Awaited<boolean | Promise<number>>;

interface User {
  name: string;
  gender:string;
  isAdmin:boolean;  
}

// Convert all properties of the User type to be optional
type PartialUser = Partial<User>;
  isAdmin : false
};

Pick<Type, Keys>

The Pick<T,K> utility type allows us to create a new type that only includes properties that we want.

We can define desired properties in the form of a string literal or a union of string literal as the second parameter of Pick<T,K>.

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

// create a type that only includes the 'name' and 'email' properties of User
type UserInfo = Pick<User, 'name' | 'email'>;

// create an instance of UserInfo
const userInfo: UserInfo = {
  name: "John Doe",
  email: "johndoe@example.com"
};

In the above example, We have created a new type UserInfo that only includes the name and email property of the User interface. This might be useful in cases when we don't want to include the password field for security reasons.

Omit<Types, Keys>

The Omit<T,K> utility type allows us to create a new type that excludes a specific property or a set of properties.

It is the opposite of Pick<T, K>.

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

// Create a type that excludes the 'password' property of User
type UserInfo = Omit<User, 'password'>;

// Create an instance of UserInfo
const userInfo: UserInfo = {
  id: 1,
  name: "John Doe",
  email: "johndoe@example.com"
};

Exclude<Type, ExcludedType>

The Exclude<Type, ExcludedType> utility allows us to exclude a set of types from another set of types.

Here, the second parameter ExcludedType , that we want to exclude from the first parameter Type. In other words, from the Type set subtract the ExcludedType set.

Omit and Exclude may look similar, but there are not.

Omit creates a new type by omitting one or more properties from an existing type, while Exclude creates a new type by excluding all values of a specific type.

/*-------------------exampe-1--------------------*/
type T0 = Exclude<"a" | "b" | "c", "b">;
// type T0 = "a" | "c"

/*-------------------exampe-2--------------------*/

// Create a new type excluding Function type
type T2 = Exclude<string | number | (() => void), Function>;
//type T2 = string | number

/*-------------------exampe-3--------------------*/
interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

interface Admin extends User {
  role: string;
}

// create a type that excludes the 'Admin' type from the set of all types
type NonAdmin = Exclude<User, Admin>;

// create an instance of NonAdmin
const nonAdmin: NonAdmin = {
  id: 1,
  name: "John Doe",
  email: "johndoe@example.com",
  password: "secret"
};

Extract<TypeA, TypeB>

The Extract<TypeA, TypeB> creates a type that is a set intersection of both TypeA and TypeB. The new type includes all types that are common in both sets.

// Create a type that includes common types in both type set
type T0 = Extract<"a" | "b" | "c", "a" | "f">;
//type T0 = "a"

type T1 = Extract<string | number | (() => void), Function>;
//type T1 = () => void

type Fruit = "apple" | "banana" | "tomato";
type Vegetable = "carrot" | "tomato" | "cucumber";

// create a type that only includes the items present in both Fruit and Vegetable
type CommonItems = Extract<Fruit, Vegetable>;
//type CommonItems = "tomato"

Record<Keys, Types>

The Record<Keys, Types> creates an object type. And this object type set property keys and values from the Keys and Types that we pass as parameters.

/*----------Example-1-------------------*/
type A =  "id" | "name";
type B = "string" ;

// create an object type
type ObjType = Record<A, B>;
/*type objType = {
         id:string   
         name:string 
    }
*/

/*----------Example-2-------------------*/
interface CatInfo {
  age: number;
  breed: string;
}
type CatName = "miffy" | "boris" | "mordred";

// Creates an object type with property key from CatName and type //from CatInfo
type ObjCat = Record<CatName, CatInfo>;
/*
 type ObjCat = {
    miffy: CatInfo;
    boris: CatInfo;
    mordred: CatInfo;
}
*/
const cats: ObjCat = {
  miffy: { age: 10, breed: "Persian" },
  boris: { age: 5, breed: "Maine Coon" },
  mordred: { age: 16, breed: "British Shorthair" },
};

In the second example above, Record<CatName, CatInfo> created an object type ObjCat. The ObjCat set property keys form CatName and property values from CatInfo and In the end, we created and cats Object of type ObjCat.

NonNullable<Type>

It creates a type by excluding null and undefined from Type.

type T0 = NonNullable<string | number | undefined>;  
//type T0 = string | number

type T1 = NonNullable<string[] | null | undefined>;
//type T1 = string[]

Readonly<Type>

The Readonly<Type> helps us to create a type that has all properties set to readonly , and these properties can be assigned only once just like like const variable.

interface Todo {
  title: string;
  description:string 
}

// Create a type with readonly properties 
type ReadOnlyTodo = Readonly<Todo>;
/*
type ReadOnlyTodo = {
    readonly title: string;
    readonly description: string;
}
*/

const todo: ReadOnlyTodo  = {
  title: "some title",
  description: "some description"
};

todo.title = "Hello"; // Error: Cannot assign to 'title' because it is a read-only property.

Parameters<Type>

The Parameters<Type> is used to extract types of function parameters in the form of a tuple.

type T1 = Parameters<(s: string) => void>;
// type T1 = [string]

type T0 = Parameters<() => string>;
// type T0 = []



function add(a: number, b: number): number {
  return a + b;
}

type AddParams = Parameters<typeof add>;
// AddParams is equal to `[number, number]`

ReturnType<Type>

The ReturnType<Type> is used to extract the return type of a function. It takes function type as a parameter.

type T0 = ReturnType<() => string>;
//type T0 = string

type T1 = ReturnType<(s: string) => void>;    
// type T1 = void

function add(a: number, b: number): number {
  return a + b;
}
type AddReturnType = ReturnType<typeof add>;
// type AddReturnType = number

Awaited<Type>

The Awaited<Type> allows us to extract the type of value that is returned from a Promise.

It is very useful in cases when we need to work with the type of returned value, rather than the Promise itself.

This utility was included in TypeScript verson 4.5

/* --------------------examle-1----------------------*/
type A = Awaited<Promise<string>>;
// type A = string

 /* --------------------examle-2----------------------*/
type B = Awaited<Promise<Promise<number>>>;
// type B = number

/* --------------------examle-3----------------------*/ 
type C = Awaited<boolean | Promise<number>>;
// type C = number | boolean

/* --------------------examle-4----------------------*/
async function fetchData(): Promise<string> {
  return Promise.resolve("Hello World");
}

type DataType = Awaited<ReturnType<typeof fetchData>>;
// type DataType = string

Conclusion:

In this article, we learned about some of the most commonly used utility types. Hope you found it useful.