Last Updated: 

Deep Omit in TypeScript: A Comprehensive Guide

In TypeScript, working with complex data types often requires the ability to exclude certain properties from an object type. While the built-in Omit utility type is useful for removing properties at the top-level, it falls short when dealing with nested objects. This is where the concept of Deep Omit comes in. Deep Omit allows us to recursively remove properties from an object type at any level of nesting. In this blog post, we will explore the fundamental concepts of Deep Omit in TypeScript, how to use it, common practices, and best practices.

Table of Contents#

  1. Fundamental Concepts of Deep Omit
  2. Usage Methods
  3. Common Practices
  4. Best Practices
  5. Conclusion
  6. References

Fundamental Concepts of Deep Omit#

The Limitation of Omit#

The built-in Omit type in TypeScript takes two generic parameters: the original type and a union of property names to omit. For example:

type Person = {
    name: string;
    age: number;
    address: {
        street: string;
        city: string;
    };
};
 
type PersonWithoutAge = Omit<Person, 'age'>;
// PersonWithoutAge is { name: string; address: { street: string; city: string; } }

However, if we want to omit a property from the nested address object, Omit won't work directly.

Deep Omit Concept#

Deep Omit is a custom type that recursively traverses an object type and removes the specified properties at any level of nesting. It uses conditional types and mapped types in TypeScript to achieve this.

type DeepOmit<T, K extends string> = T extends object
  ? {
      [P in keyof T as P extends K ? never : P]: DeepOmit<T[P], K>;
    }
  : T;

In this type definition:

  • If T is an object, we use a mapped type to iterate over its keys. If a key P is in the set of keys K to omit, we exclude it. Otherwise, we recursively apply DeepOmit to the value of that key.
  • If T is not an object, we simply return T as it is.

Usage Methods#

Basic Usage#

Let's use the DeepOmit type we defined above to omit a nested property.

type Person = {
    name: string;
    age: number;
    address: {
        street: string;
        city: string;
    };
};
 
type PersonWithoutStreet = DeepOmit<Person, 'street'>;
// PersonWithoutStreet is { name: string; age: number; address: { city: string; } }

Omitting Multiple Properties#

We can also omit multiple properties by providing a union of property names.

type PersonWithoutAgeAndStreet = DeepOmit<Person, 'age' | 'street'>;
// PersonWithoutAgeAndStreet is { name: string; address: { city: string; } }

Common Practices#

Using with API Responses#

When working with API responses, we might want to remove sensitive or unnecessary data before using it in our application.

// API response type
type APIResponse = {
    user: {
        id: number;
        name: string;
        privateInfo: {
            email: string;
            phone: string;
        };
    };
    meta: {
        timestamp: number;
    };
};
 
// Omit sensitive data
type SafeAPIResponse = DeepOmit<APIResponse, 'email' | 'phone'>;

Working with Form Data#

When handling form data, we might want to remove some fields that are not relevant for a particular operation.

type FormData = {
    personal: {
        firstName: string;
        lastName: string;
        ssn: string; // Sensitive data
    };
    contact: {
        email: string;
        phone: string;
    };
};
 
type SafeFormData = DeepOmit<FormData, 'ssn'>;

Best Practices#

Use Descriptive Names#

When defining the keys to omit, use descriptive names. For example, instead of using single-letter keys, use full property names like 'userEmail' or 'productPrice'.

Test the Type#

Before using a DeepOmit type in production code, test it thoroughly. You can use TypeScript's type assertions to check if the resulting type is as expected.

type TestType = {
    a: {
        b: string;
        c: number;
    };
    d: boolean;
};
 
type ResultType = DeepOmit<TestType, 'b'>;
 
// Assert the type
const test: ResultType = {
    a: {
        c: 123
    },
    d: true
};

Consider Performance#

Deep Omit operations can be computationally expensive for very large and deeply nested objects. If performance is a concern, consider alternative approaches such as filtering the data at runtime instead of using type-level operations.

Conclusion#

Deep Omit in TypeScript is a powerful tool for working with complex object types. It allows us to recursively remove properties from nested objects, which is not possible with the built-in Omit type. By understanding the fundamental concepts, usage methods, common practices, and best practices, you can effectively use Deep Omit in your TypeScript projects to manage and manipulate data types.

References#