Search code examples
typescripttype-mapping

How can I replace all types of a nested object recursively based on some conditions?


In the Further Exploration example on TypeScript website they show us a way to replace the type of a property with a different type based on some conditions.

How can we do the same but in a recursive way? i.e. not only map the first level properties, but any nested property that passes the check.

The example:

type ExtractPII<Type> = {
  [Property in keyof Type]: Type[Property] extends { pii: true } ? true : false;
};

type DBFields = {
  id: { format: "incrementing" };
  name: { type: string; pii: true };
};

type ObjectsNeedingGDPRDeletion = ExtractPII<DBFields>;
// type ObjectsNeedingGDPRDeletion = { id: false; name: true; }

What I need:

type DBFields = {
  id: { format: "incrementing", foo: { type: string; pii: true } };
  name: { type: string; pii: true };
};

type ObjectsNeedingGDPRDeletion = ExtractPII<DBFields>; 
// type ObjectsNeedingGDPRDeletion = { id: { format: string; foo: true }; name: true; }

Solution

  • In the false case, you just need to execute another check to see if the type is an object, and then call ExtractPii on that property.

    type ExtractPII<T> = {
      [P in keyof T]: T[P] extends { pii: true } 
        ? true 
        : T[P] extends object 
          ? ExtractPII<T[P]> 
          : T[P];
    };
    

    This will results in:

    type DBFields = {
      id: { format: "incrementing", foo: { type: string; pii: true } };
      name: { type: string; pii: true };
    };
    
    type ObjectsNeedingGDPRDeletion = ExtractPII<DBFields>;
    
    const a: ObjectsNeedingGDPRDeletion = {
        name: true,
        id: {
            foo: true,
            format: "incrementing"
        }
    }
    

    If you want in the false case the value false, then just replace T[P] with false:

    type ExtractPII<T> = {
      [P in keyof T]: T[P] extends { pii: true } 
        ? true 
        : T[P] extends object 
          ? ExtractPII<T[P]> 
          : false;
    };