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.
Discover how at OpenReplay.com.
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.
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:
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
orSSN
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’sid
,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 likePartial
orRequired
allows for precise control over property optionality. For example, we can useOmit
to exclude certain properties andPartial
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
OverOmit
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 usePick
instead ofOmit
. For example, if we want to excludeid
,email
, andpassword
from theUser
type below, it means we want to include onlyname
andaddress
. UsingPick
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 ofOmit
can lead to complex and hard-to-maintain types. It is beneficial to useOmit
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.