Back

How to use Typescript's 'omit' utility type

How to use Typescript's 'omit' utility type

A commonly used utility type in TypeScript is Omit. This type allows developers to form a new type from an existing one by excluding specific fields. This tutorial shows how to use it to handle different scenarios, from simple to complex, and examines practical applications to see some real-life reasons why you might need to use it.

TypeScript is a version of JavaScript that allows developers to define the types (data structure) for different elements—variables, class constructors, function arguments, function return statements, etc. One of its useful features is utility types. These help modify existing types to create new ones, so there’s no need to create entirely new types from scratch. Instead, existing types can be adjusted and reused.

Omit creates a new type by picking all the properties from an existing type and excluding a specified list of keys. Its first argument is the existing type, and the second argument is a union of the types to be excluded from it. The syntax is as follows:

Omit<Type, Keys>

In the next sections, we’ll explore various scenarios where Omit can be used to demonstrate how to apply it.

Omitting a Single Property

Suppose we have a type called User, and we want to create another type, UserWithoutEmail, with all the keys in User except for one key, email, as shown in the image below.

image

We can accomplish this using the Omit utility type:

type User = {
  id: string;
  name: string;
  email: string;
  age: number;
};

type UserWithoutEmail = Omit<User, "email">;

// valid use
const validUser: UserWithoutEmail = {
  id: "1",
  name: "John",
  age: 20,
};

// invalid use
const invalidUser: UserWithoutEmail = {
  id: "1",
  name: "John",
  age: 20,
  email: "johndoe@gmail.com", // Property 'email' does not exist in type 'UserWithoutEmail'.
};

We derive the UserWithoutEmail type from the User type by excluding the email property. This means it includes all other properties from User, such as id, name, and age.

Omitting Multiple Properties

Sometimes, we want to omit more than one key from the base key. For example, we have a type User, and want to exclude the sensitive info in it by forming UserWithoutSensitiveInfo as shown in the image below:

image

We’ll accomplish this as follows:

type User = {
  id: string;
  name: string;
  email: string;
  age: number;
  password: string;
};

type UserWithoutSensitiveInfo = Omit<User, "email" | "password">;

// valid use
const validUser: UserWithoutSensitiveInfo = {
  id: "1",
  name: "John",
  age: 20,
};

// invalid use
const invalidUser: UserWithoutSensitiveInfo = {
  id: "1",
  name: "John",
  age: 20,
  email: "johndoe@gmail.com", // Property 'email' does not exist in type 'UserWithoutSensitiveInfo'.
  password: "password", // Property 'password' does not exist in type 'UserWithoutSensitiveInfo'.
};

We create the UserWithoutSensitiveInfo type by using Omit to exclude the email and password properties from the User type.

Omitting Specific Properties from Function Arguments

Imagine we have a User type and a function that updates a user’s profile. We want to exclude certain properties from being updated, such as id and email, since these should remain constant. We can use Omit to create a type that excludes these properties from the function’s argument type.

type User = {
  id: string;
  name: string;
  email: string;
  age: number;
  role: string;
};

type UpdateUserProfile = Omit<User, "id" | "email">;

function updateUser(userId: string, updates: UpdateUserProfile) {
  // Logic to update user profile goes here
  console.log(`Updating user ${userId} with:`, updates);
}

// Valid use
updateUser("1", {
  name: "Jane Doe",
  age: 28,
  role: "admin",
});

// Invalid use
updateUser("1", {
  id: "2", // Error: Property 'id' does not exist in type 'UpdateUserProfile'.
  email: "jane@example.com", // Error: Property 'email' does not exist in type 'UpdateUserProfile'.
  name: "Jane Doe",
  age: 28,
  role: "admin",
});

The UpdateUserProfile type, used with the updateUser function, excludes id and email, allowing updates only to name, age, and role.

Omitting Class Properties

Sometimes, we want to restrict certain properties from being passed when creating an instance of a class. For example, if the id of a user is generated internally and should not be set by the user, we can use the Omit utility type to exclude it from the constructor arguments.

class User {
  id: string;
  name: string;
  email: string;
  password: string;

  constructor(data: Omit<User, "id">) {
    this.id = this.generateId(); // Assume this method generates a unique ID
    this.name = data.name;
    this.email = data.email;
    this.password = data.password;
 }

  private generateId(): string {
    // A simple example of generating a unique ID
    return Math.random().toString(36).substr(2, 9);
 }
}

// Example usage
const userData: Omit<User, "id"> = {
  name: "John Doe",
  email: "john.doe@example.com",
  password: "password123",
};

const user = new User(userData);

console.log(user);
// Output:
// User {
//   id: 'generated_id',
//   name: 'John Doe',
//   email: 'john.doe@example.com',
//   password: 'password123',
// }

In this example, we create a User class where the id is generated automatically. We use Omit to exclude the id property from the constructor’s parameter type.

Working with Nested Properties

Suppose we have a UserProfile type with nested properties, and we want to create a new type that excludes certain nested properties:

type Address = {
  street: string;
  city: string;
  postalCode: string;
};

type UserProfile = {
  id: string;
  name: string;
  email: string;
  address: Address;
};

// Create a type that omits the nested 'postalCode' property
type UserProfileWithoutPostalCode = Omit<UserProfile, "address"> & {
  address: Omit<Address, "postalCode">;
};

// Example usage
const userProfile: UserProfileWithoutPostalCode = {
  id: "1",
  name: "Alice",
  email: "alice@example.com",
  address: {
    street: "123 Main St",
    city: "Wonderland",
    // postalCode is excluded here
 },
};

console.log(userProfile);
// Output:
// {
//   id: '1',
//   name: 'Alice',
//   email: 'alice@example.com',
//   address: {
//     street: '123 Main St',
//     city: 'Wonderland'
//   },
// }

To create the UserProfileWithoutPostalCode type, we first omit the address key from the UserProfile type. This results in a type that includes all properties of UserProfile except address. We then redefine the address property by creating a new type where postalCode is omitted from the address. Finally, we combine these two types using the intersection (&) operator. The resulting UserProfileWithoutPostalCode type has all the original properties of UserProfile, but with the address property excluding the postalCode key.

Combining Omit with Other Utility Types

We can combine Omit with other utility types to create more custom types:

CombiningOmit and Partial

We can combine Omit with Partial to exclude certain properties and make the remaining ones optional:

type User = {
  id: string;
  name: string;
  email: string;
};

type OptionalUser = Partial<Omit<User, 'id'>>;

We create the OptionalUser type by first omitting the id property from the User type, resulting in a type with only the name and email properties. Applying Partial then makes these remaining properties optional. Thus, OptionalUser allows for an object where name and email are optional and id is not present.

Combining Omit and Required

We can use Required with Omit to exclude properties and ensure that the remaining ones are required:

type User = {
  id?: string;
  name?: string;
  email?: string;
};

type RequiredUser = Required<Omit<User, 'id'>>;

Here, we use Required and Omit to ensure that id is not part of RequiredUser, and that the other optional properties in User - name and email, are required properties in RequiredUser.

In general, Omit can be used anywhere a regular type is used because it returns a regular type. It creates a new type by removing specified properties, making it suitable for type annotations, function parameters, and object definitions.

Note on interface Compatibility

In TypeScript, Omit is compatible with interfaces just as it is with types. When using Omit with interfaces in TypeScript, the resulting type can sometimes be represented as an interface or a type alias, depending on the context. Typically, Omit produces a type alias because it performs a type transformation that is more suited to type aliases. However, it is possible to use Omit in a way that integrates with interfaces, either by extending them or combining them with other types.

For example, when using Omit directly on an interface and extending it, the result can be represented as an interface:

// Define the original interface
interface User {
  id: string;
  name: string;
  email: string;
}

// Apply Omit and extend the result as an interface
interface UserWithoutEmail extends Omit<User, "email"> {}

// Example usage
const user: UserWithoutEmail = {
  id: "1",
  name: "Alice",
  // email is omitted
};

In this case, UserWithoutEmail is an interface that extends the type produced by Omit<User, 'email'>. Even though Omit produces a type, it is used here to define an interface.

On the other hand, if Omit is used in a way that directly creates a type alias, the result will be a type:

// Define the original interface
interface User {
  id: string;
  name: string;
  email: string;
}

// Create a type alias using Omit
type UserWithoutEmail = Omit<User, "email">;

// Example usage
const user: UserWithoutEmail = {
  id: "1",
  name: "Alice",
  // email is omitted
};

In this example, UserWithoutEmail is directly created as a type alias using Omit<User, 'email'>. Here, Omit returns a type, and UserWithoutEmail is defined as a type alias rather than an interface.

Practical Use Cases for Omit

The Omit utility type serves several practical purposes in different scenarios, including:

  • Removing Sensitive Information: When handling user data, we might need to exclude sensitive fields such as password or SSN from objects that are exposed to the public or shared with external services. We can ensure these fields are not inadvertently included using’ Omit’.
  • Creating API Response Types: For API design, Omit can be used to customize response types by removing internal properties that should not be visible to clients. This helps in exposing only the relevant data, such as excluding internal database identifiers or status codes.
  • Simplifying Form Handling: In form management, Omit can help by removing irrelevant fields from form types. For example, if a registration form does not require a user’s id, Omit can be used to exclude this property, resulting in a more focused and simpler form type.
  • Refactoring Legacy Code: As codebases evolve, certain properties might become obsolete. Omit facilitates the process of modernizing code by removing deprecated properties, making it easier to update and maintain the codebase.
  • Handling Optional and Required Properties: Combining Omit with utility types like Partial or Required allows for precise control over property optionality. For example, we can use Omit to exclude certain properties and Partial to make the remaining properties optional, tailoring types to meet specific requirements.

It’s important to remember that Omit, like other TypeScript types, is checked only during development. TypeScript enforces these checks in the editor, during compilation, and when linting is enabled. However, these checks do not impact runtime behavior. For instance, consider the following example:

type User = {
  id: string;
  name: string;
  email: string;
};

type UserWithoutId = Omit<User, 'id'>;

// Example usage
const user: UserWithoutId = {
  name: "Alice",
  email: "alice@example.com",
  id: "1" // This will show an error in the editor during compilation, and if linting is enabled
};

In this case, TypeScript will flag the id property as an error in the editor during compilation and if linting is active. However, if TypeScript linting is disabled, the id property will still be included in the user object, and the Omit type will not enforce the exclusion at runtime. Therefore, it is crucial to ensure that TypeScript linting is enabled and followed to catch such issues during development.

Best Practices

When using the Omit utility type in TypeScript, it’s important to know common pitfalls and when to avoid using it. Here are some best practices to keep in mind:

  • Prefer Pick Over Omit When More Properties Are Included: If you need to include a majority of properties from a type and only exclude a few, it’s often more practical to use Pick instead of Omit. For example, if we want to exclude id, email, and password from the User type below, it means we want to include only name and address. Using Pick is more straightforward:
type User = {
  id: string;
  name: string;
  email: string;
  password: string;
  address: string;
};

// Using Pick to include only selected properties
type UserContactInfo = Pick<User, "name" | "address">;

// instead of
type UserContactInfo = Omit<User, "id" | "email" | "password">;
  • Avoid Omitting Nonexistent Properties: Ensure that the properties being omitted actually exist in the original type. Omitting a nonexistent property will result in TypeScript errors.
  • Be Cautious with Complex Nested Types: When working with complex nested types, it’s important to break them down into simpler components to avoid errors and improve clarity. Large, deeply nested types can become difficult to manage and understand.
  • Avoid Overusing Omit: Excessive use of Omit can lead to complex and hard-to-maintain types. It is beneficial to use Omit judiciously and prefer more straightforward type manipulations when possible.

Conclusion

This tutorial explored the Omit utility type in TypeScript and its role in managing types by removing specific properties. We examined how Omit works, its syntax, and various practical applications. Additionally, we discussed best practices for using Omit effectively, helping to ensure clearer and more maintainable code.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay