2

Consider the following:

type U = {a: string, b: string};

type T = U & ({c: string, d?: never}|{c?: never, d: string});
type E = Exclude<T, {a: string}>;
type O = Omit<T, "a">;

const t1: T = {a: "a", b: "b", c: "c", d: "d"}; // error - expected
const t2: T = {a: "a", b: "b", c: "c"}; // ok - expected
const t3: T = {a: "a", b: "b", d: "d"}; // ok - expected

const e1: E = {b: "foo", c: "c", d: "d"}; // error - expected
const e2: E = {b: "foo", c: "c"}; // error: E is never
const e3: E = {b: "foo", d: "d"}; // error: E is never

const o1: O = {b: "foo", c: "c", d: "d"}; // ok - unexpected!
const o2: O = {b: "foo", c: "c"}; // ok
const o3: O = {b: "foo", d: "d"}; // ok

Type T is defined as a object containing properties a and b and either c or d, but not both.

I am trying to construct a type that is identical to T except it doesn’t have the a property. I tried both Exclude and Omit, as it is evident from the above, neither gives me what I need.

Is there a way to remove a property from a black box type T and keep everything else about it the same without knowing the structure of type T in advance?

Also Typescript playground here.

Edit: Fixed the original type; added more test usages

Edit #2: This question is different from Typescript: Omit a property from all interfaces in a union, but keep the union structure because:

  • The type in question is not a straight union but rather an intersection containing a union on one side
  • It also addresses the issue of removing a property of from an arbitrary “black box” type without changing any other semantics of it.
Levi Haskell
  • 795
  • 8
  • 20

1 Answers1

1

Both Omit and Exclude work fine over simple types (types that do not require unions or other complex compositions)

When the base type contains unions, it is called Distributive conditional types in typescript

A working example, following the playground provided would look something like this:

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


type U = {a: string, b: string};

type T = U & ({c: string, d?: never}|{c?: never, d: string});
type D = DistributiveOmit<T, "a">;

const d1: D = {b: "foo", c: "c", d: "d"}; // error - expected
const d2: D = {b: "foo", c: "c"}; // ok
const d3: D = {b: "foo", d: "d"}; // ok

You can see it working in Playground here

Jhilton
  • 420
  • 3
  • 7