typescript

Omit a property from all interfaces in a union, but keep the union structure


I have a few interfaces defined like so:

interface A {
  toRemove: string;
  key1: "this1";
  key2: number;
}
interface B {
  toRemove: string;
  key1: "this2";
  key3: string;
}

And a union of both interfaces:

type C = A|B;

What I want to do is to remove the toRemove key from both interfaces through C, something like this:

type CC = Omit<A, "toRemove">|Omit<B, "toRemove">;

But without having to omit the key from both interfaces. This would be ideal:

type CC = Omit<C, "toRemove">;

But, unfortunately, CC will be of type Pick<A|B, "key1">, where key1 is the key present in both interfaces.

In essence, what I'm trying to achieve is a type of "function" to transform:

A1|A2|...|An

into:

Omit<A1, K keyof A1>|Omit<A1, K keyof A2>|...|Omit<An, K keyof An>

I came across this answer and I have a feeling that part of what I need is somewhere in there, but I don't really understand what's going on in that code.


Solution

  • You want to distribute the Omit across a union. Luckily, you can use distributive conditional types to achieve this:

    type DistributiveOmit<T, K extends keyof any> = T extends any
      ? Omit<T, K>
      : never;
    

    The T extends any construction looks like it doesn't do much, but since T is a type parameter, it distributes the conditional type across any union constituents of T.

    Let's test it:

    type CC = DistributiveOmit<C, "toRemove">;
    // type CC = Pick<A, "key1" | "key2"> | Pick<B, "key1" | "key3">
    

    You can verify that this is equivalent to the CC type you want.

    Link to code