Skip to main content

TypeScript: Utility Types

Utility Types

📘 typescriptlang.org > utility-types

TypeScript provides a set of utility types that can be used to manipulate and transform existing types. These utility types are generic and can be applied to any type to create new types based on the original.

Partial<T>

📘 typescriptlang.org > Partial

  • Description: Constructs a type with all properties of T set to optional.
  • Usage: When you want to make some or all properties of a type optional.
interface User {
name: string;
age: number;
email: string;
}

function createUser(user: Partial<User>): User {
return {
name: "John Doe",
age: 30,
email: "john.doe@example.com",
...user,
};
}

const newUser = createUser({ name: "Jane Doe" });

console.log(newUser);
// Output: { name: 'Jane Doe', age: 30, email: 'john.doe@example.com' }

Pick<T, K>

📘 typescriptlang.org > Pick

  • Description: Constructs a type by picking a set of properties K from T.
  • Usage: When you need to select specific properties from a type.
interface Todo {
title: string;
description: string;
completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;

const todo: TodoPreview = {
title: "Clean room",
completed: false,
};

Omit<T, K>

📘 typescriptlang.org > Omit

  • Description: Constructs a type by omitting a set of properties K from T.
  • Usage: When you want to exclude specific properties from a type.
interface Todo {
title: string;
description: string;
completed: boolean;
createdAt: number;
}

type TodoPreview = Omit<Todo, "description">;

const todo: TodoPreview = {
title: "Clean room",
completed: false,
createdAt: 1615544252770,
};

type TodoInfo = Omit<Todo, "completed" | "createdAt">;

const todoInfo: TodoInfo = {
title: "Pick up kids",
description: "Kindergarten closes at 5pm",
};

Readonly<T>

📘 typescriptlang.org > Readonly

  • Description: Constructs a type with all properties of T set to readonly.
  • Usage: When you want to make all properties of a type read-only.
interface Todo {
title: string;
}

const todo: Readonly<Todo> = {
title: "Delete inactive users",
};

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

Required<T>

📘 typescriptlang.org > Required

  • Description: Constructs a type with all properties of T set to required.
  • Usage: When you want to make all properties of a type required.
interface Props {
a?: number;
b?: string;
}

const obj: Props = { a: 5 };

const obj2: Required<Props> = { a: 5 };
// ERROR: Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.

Record<K, T>

📘 typescriptlang.org > Record

Constructs an object type whose property keys are Keys and whose property values are Type. This utility can be used to map the properties of a type to another type.

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

type CatName = "miffy" | "boris" | "mordred";

const cats: Record<CatName, CatInfo> = {
miffy: { age: 10, breed: "Persian" },
boris: { age: 5, breed: "Maine Coon" },
mordred: { age: 16, breed: "British Shorthair" },
};

cats.boris; // const cats: Record<CatName, CatInfo>

Exclude<T, U>

The Exclude<T, U> utility type constructs a type by excluding from T all properties that are assignable to U. This can be useful when you want to create a type that omits certain properties or values.

  • Purpose: The Exclude type helps in creating a type that represents all the possible values of type T, except those that are also assignable to type U.
  • Usage: Typically used when you need to filter out certain values or types from a union type.
type Status = "active" | "inactive" | "pending" | "deleted";

// Use Exclude to create a type that excludes 'deleted' and 'inactive'
type ActiveStatus = Exclude<Status, "deleted" | "inactive">;

let status1: ActiveStatus = "active"; // Valid
let status2: ActiveStatus = "pending"; // Valid
let status3: ActiveStatus = "deleted"; // Error: Type '"deleted"' is not assignable to type 'ActiveStatus'.

Extract<T, U>

The Extract<T, U> utility type constructs a type by extracting from T all properties that are assignable to U. This is useful when you want to create a type that consists of only the properties or values that are common between two types.

  • Purpose: The Extract type helps in creating a type that represents all the possible values of type T that are also assignable to type U.
  • Usage: Typically used when you need to filter out only certain values or types from a union type based on another type.
type Status = "active" | "inactive" | "pending" | "deleted";

// Use Extract to create a type that includes only 'active' and 'pending'
type ActiveStatus = Extract<Status, "active" | "pending">;

let status1: ActiveStatus = "active"; // Valid
let status2: ActiveStatus = "pending"; // Valid
let status3: ActiveStatus = "deleted"; // Error: Type '"deleted"' is not assignable to type 'ActiveStatus'.

NonNullable<T>

The NonNullable<T> utility type constructs a type by excluding null and undefined from T. This is useful when you want to ensure that a type does not include null or undefined values.

type MyType = string | number | null | undefined;

// Use NonNullable to create a type that excludes null and undefined
type NonNullableMyType = NonNullable<MyType>;

let value1: NonNullableMyType = "Hello"; // Valid
let value2: NonNullableMyType = 42; // Valid
let value3: NonNullableMyType = null; // Error: Type 'null' is not assignable to type 'NonNullableMyType'.
let value4: NonNullableMyType = undefined; // Error: Type 'undefined' is not assignable to type 'NonNullableMyType'.

ReturnType<T>

The ReturnType<T> utility type constructs a type consisting of the return type of function T. This is useful when you want to extract the return type of a given function type.

// Define a function
function getUser() {
return {
name: "John",
age: 30,
};
}

// Use ReturnType to create a type that matches the return type of getUser
type User = ReturnType<typeof getUser>;

const user: User = {
name: "John",
age: 30,
}; // Valid

const anotherUser: User = {
name: "Jane",
age: "unknown", // Error: Type 'string' is not assignable to type 'number'.
};

InstanceType<T>

The InstanceType<T> utility type constructs a type consisting of the instance type of a constructor function type T. This is useful when you want to create a type that represents the type of an instance created by a given constructor function or class.

Suppose you have a class and you want to create a type that matches the instances of this class.

// Define a class
class User {
name: string;
age: number;

constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}

// Use InstanceType to create a type that matches the instance type of User
type UserType = InstanceType<typeof User>;

const user: UserType = new User("John", 30); // Valid

const anotherUser: UserType = {
name: "Jane",
age: 25,
}; // Valid

const invalidUser: UserType = {
name: "Jack",
age: "unknown", // Error: Type 'string' is not assignable to type 'number'.
};

Parameters<T>

The Parameters<T> utility type constructs a tuple type from the types used in the parameters of function T. This is useful when you want to extract the parameter types of a given function type.

Suppose you have a function and you want to create a type that matches the parameters of this function.

// Define a function
function createUser(name: string, age: number, isAdmin: boolean) {
return {
name,
age,
isAdmin,
};
}

// Use Parameters to create a type that matches the parameter types of createUser
type CreateUserParams = Parameters<typeof createUser>;

const params: CreateUserParams = ["John", 30, true]; // Valid

const invalidParams: CreateUserParams = ["Jane", "25", false]; // Error: Type 'string' is not assignable to type 'number'.

ConstructorParameters<T>

The ConstructorParameters<T> utility type constructs a tuple type from the types used in the parameters of a constructor function type T. This is useful when you want to extract the parameter types of a given constructor function or class.

Suppose you have a class with a constructor and you want to create a type that matches the parameters of this constructor.

// Define a class
class User {
name: string;
age: number;
isAdmin: boolean;

constructor(name: string, age: number, isAdmin: boolean) {
this.name = name;
this.age = age;
this.isAdmin = isAdmin;
}
}

// Use ConstructorParameters to create a type that matches the parameter types of User's constructor
type UserConstructorParams = ConstructorParameters<typeof User>;

const params: UserConstructorParams = ["John", 30, true]; // Valid

const invalidParams: UserConstructorParams = ["Jane", "25", false]; // Error: Type 'string' is not assignable to type 'number'.

ThisParameterType<T>

The ThisParameterType<T> utility type extracts the type of the this parameter for a function type T. This is useful when you want to work with or modify the this context type of a function.

Suppose you have an object with a method that uses this, and you want to extract the type of the this parameter.

// Define an object with a method that uses `this`
const obj = {
name: "Alice",
greet(this: { name: string }, greeting: string) {
return `${greeting}, ${this.name}`;
},
};

// Use ThisParameterType to extract the type of the `this` parameter from the greet method
type GreetThisType = ThisParameterType<typeof obj.greet>;

const newThis: GreetThisType = { name: "Bob" }; // Valid

// Incorrect usage example
const incorrectThis: GreetThisType = { name: 123 }; // Error: Type 'number' is not assignable to type 'string'.

OmitThisParameter<T>

The OmitThisParameter<T> utility type constructs a type by removing the this parameter from a function type T. This is useful when you want to create a version of a function type that does not require a specific this context.

Suppose you have an object with a method that uses this, and you want to create a version of this method that does not have the this parameter.

// Define an object with a method that uses `this`
const obj = {
name: "Alice",
greet(this: { name: string }, greeting: string) {
return `${greeting}, ${this.name}`;
},
};

// Use OmitThisParameter to create a type for the greet method without the `this` parameter
type GreetWithoutThis = OmitThisParameter<typeof obj.greet>;

// Create a function that matches the new type
const greetFn: GreetWithoutThis = (greeting: string) => {
return `${greeting}, world!`;
};

console.log(greetFn("Hello")); // Output: Hello, world!

ThisType<T>

The ThisType<T> utility type is a marker interface used in conjunction with the noImplicitThis TypeScript compiler option. It does not create a new type directly but rather allows you to specify the type of this within an object literal's methods. This is particularly useful for objects with methods that rely on a specific this context.

Suppose you have an object with methods that rely on a specific this context, and you want to ensure that this is correctly typed.

interface Person {
name: string;
age: number;
}

// Use ThisType to specify the type of `this` in an object literal
const person: Person & ThisType<Person> = {
name: "Alice",
age: 25,
setName(newName: string) {
this.name = newName; // `this` is correctly inferred as Person
},
setAge(newAge: number) {
this.age = newAge; // `this` is correctly inferred as Person
},
};

// Correct usage
person.setName("Bob");
person.setAge(30);

console.log(person.name); // Output: Bob
console.log(person.age); // Output: 30

// Incorrect usage (TypeScript will throw an error if `noImplicitThis` is enabled)
// const setNameFn = person.setName;
// setNameFn('Charlie'); // Error: `this` implicitly has type 'any' because it does not have a type annotation.

Brand<T, Brand>

The Brand<T, Brand> utility type is a pattern used to create nominal types in TypeScript. Nominal typing allows you to create distinct types that are not interchangeable, even if they have the same underlying structure. This can be useful for creating safer APIs by ensuring that values of different types are not accidentally mixed.

Suppose you want to create distinct types for user IDs and order IDs, even though both are represented as strings.

// Define the Brand type
type Brand<T, Brand> = T & { __brand: Brand };

// Create distinct types for UserId and OrderId using the Brand type
type UserId = Brand<string, "UserId">;
type OrderId = Brand<string, "OrderId">;

// Functions that use the branded types
function getUserById(id: UserId) {
// Implementation here...
console.log(`Fetching user with ID: ${id}`);
}

function getOrderById(id: OrderId) {
// Implementation here...
console.log(`Fetching order with ID: ${id}`);
}

// Correct usage
const userId: UserId = "user123" as UserId;
const orderId: OrderId = "order456" as OrderId;

getUserById(userId); // Valid
getOrderById(orderId); // Valid

// Incorrect usage (TypeScript will throw an error)
getUserById(orderId); // Error: Argument of type 'OrderId' is not assignable to parameter of type 'UserId'.
getOrderById(userId); // Error: Argument of type 'UserId' is not assignable to parameter of type 'OrderId'.

Unbrand<T>

The Unbrand<T> utility type is a pattern used to remove the branding from a nominal type, reverting it back to its underlying type. This can be useful when you need to operate on the underlying type of a branded type, such as for serialization or comparison.

Suppose you have distinct branded types for user IDs and order IDs and want to revert them to their underlying string type for certain operations.

// Define the Brand type
type Brand<T, Brand> = T & { __brand: Brand };

// Define the Unbrand type
type Unbrand<T> = T extends Brand<infer U, any> ? U : T;

// Create distinct types for UserId and OrderId using the Brand type
type UserId = Brand<string, "UserId">;
type OrderId = Brand<string, "OrderId">;

// Functions that use the branded types
function getUserById(id: UserId) {
console.log(`Fetching user with ID: ${id}`);
}

function getOrderById(id: OrderId) {
console.log(`Fetching order with ID: ${id}`);
}

// Function that works with unbranded types
function logId(id: string) {
console.log(`ID: ${id}`);
}

// Correct usage
const userId: UserId = "user123" as UserId;
const orderId: OrderId = "order456" as OrderId;

getUserById(userId); // Valid
getOrderById(orderId); // Valid

// Using the Unbrand type to get the underlying type
const unbrandedUserId: Unbrand<UserId> = userId; // unbrandedUserId is of type string
const unbrandedOrderId: Unbrand<OrderId> = orderId; // unbrandedOrderId is of type string

// Log the unbranded IDs
logId(unbrandedUserId); // Output: ID: user123
logId(unbrandedOrderId); // Output: ID: order456

// Incorrect usage (TypeScript will throw an error)
getUserById(orderId); // Error: Argument of type 'OrderId' is not assignable to parameter of type 'UserId'.
getOrderById(userId); // Error: Argument of type 'UserId' is not assignable to parameter of type 'OrderId'.

If<C, T, F>

The If<C, T, F> utility type is a conditional type that selects one of two types based on a condition C. If the condition C is true, it returns type T; otherwise, it returns type F. This is useful for creating types that depend on a compile-time boolean condition.

Suppose you want to create a type that is either string or number based on a boolean condition.

// Define the If type
type If<C extends boolean, T, F> = C extends true ? T : F;

// Create a conditional type based on a boolean condition
type ConditionalType<C extends boolean> = If<C, string, number>;

// Usage examples
type TrueCondition = ConditionalType<true>; // TrueCondition is string
type FalseCondition = ConditionalType<false>; // FalseCondition is number

// Demonstrate with variables
let trueVariable: TrueCondition = "This is a string"; // Valid
let falseVariable: FalseCondition = 42; // Valid

// Incorrect usage examples
let invalidTrueVariable: TrueCondition = 42; // Error: Type 'number' is not assignable to type 'string'.
let invalidFalseVariable: FalseCondition = "Not a number"; // Error: Type 'string' is not assignable to type 'number'.

Automated Amazon Reports

Automatically download Amazon Seller and Advertising reports to a private database. View beautiful, on demand, exportable performance reports.

bidbear.io