typescripttypescript-genericsdiscriminated-union

Conditionally remove optional attribute on property in discriminated union type


So I have this tagged union type:

type A = {
    type: 'a';
    attr1: number;
};

type B = {
    type: 'b';
    attr1: string;
    attr2: boolean;
};

type C = {
    type: 'c';
    attr5: object;
    attr6: boolean;
};

type U = A | B | C;

I want to have a partial object of that type. So I tried using partial:

type PartialU = Partial<U>;

But this poses problem, as the type attribute is also marked as optional:

type PartialU = Partial<U>;
//   ^?
//   {
//      type?: 'a';
//      attr1?: number;
//   } | {
//      type?: 'b';
//      attr1?: string;
//      attr2?: boolean;
//   } | {
//      type?: 'c';
//      attr5?: object;
//      attr6?: boolean;
//   }

This causes the tagged union to not really be tagged anymore, since values are not exclusive to each alternative, as any of them can be undefined, defeating the purpose of the union.

I would prefer a type like this:

{
   type: 'a';
   attr1?: number;
} | {
   type: 'b';
   attr1?: string;
   attr2?: boolean;
} | {
   type: 'c';
   attr5?: object;
   attr6?: boolean;
}

Is there an obvious way to achieve this?


Solution

  • How about making an intersection between Partial<U> and Pick<U, 'type'>?

    You can make a utility type such as:

    type PartialExcept<T extends object, K extends keyof T> = Partial<T> & Pick<T, K>
    

    And use it in your case as:

    type PartialU = PartialExcept<U, 'type'>
    

    This will make sure that type is not a partial but the rest of the object is.