DEV Community

Frédéric Leroy
Frédéric Leroy

Posted on

Typescript type grouping a union type of objects by any property discriminating these objects.

MappingByDiscriminator

Example:

type Car = {
  kind: "car",
  ...
};

type Truck = {
  kind: "truck",
  ...
};

type Vehicle = Car | Truck;
Enter fullscreen mode Exit fullscreen mode

Then

type VehiclesByKind = MappingByDiscriminator<Vehicle,"kind">;
Enter fullscreen mode Exit fullscreen mode

Will produce :

type VehiclesByKind = {
    car: Car[],
    truck: Truck[],
}
Enter fullscreen mode Exit fullscreen mode

Mapping union type on discriminator
using the UnionMemberByDiscriminator and GetProperty described below,

type MappingByDiscriminator< E extends object, D extends keyof E > = 
   GetPropertyType<E,D> extends string | number ?
   {
      [d in GetPropertyType<E, D>]: UnionMemberByDiscriminator<E, D, d>[];
    }
  : never;
Enter fullscreen mode Exit fullscreen mode

Will map types in a union by a discriminator.

GetProperty

Get the type of an object property with generics

type GetProperty<E extends object, P extends keyof E> = 
  E extends { [key in P]: infer V } ? V : never;
Enter fullscreen mode Exit fullscreen mode

Example:

type Entity = {
   kind: 'human' | 'animal',
   size: 'microscopic' | 'small' | 'big' | 'giant',
}
Enter fullscreen mode Exit fullscreen mode

Then

type SizeType = GetProperty<Entity,"size">
Enter fullscreen mode Exit fullscreen mode

Will produce

type SizeType = 'microscopic' | 'small' | 'big' | 'giant';
Enter fullscreen mode Exit fullscreen mode

UnionMemberByDiscriminator

Extract a specific type from a union for a given discriminator value (can be used with generics)

type UnionMemberByDiscriminator<
  E extends object,
  D extends keyof E,
  DValue extends string | number
> = E extends {[key in D]:DValue} ? E : never;
Enter fullscreen mode Exit fullscreen mode

Example:

type Car = {
  kind: "car",
  ...
};

type Truck = {
  kind: "truck",
  ...
};

type Vehicle = Car | Truck;

type InferredCar = 
   UnionMemberByDiscriminator<Vehicle, "kind", "car">
Enter fullscreen mode Exit fullscreen mode

Will produce

type InferredCar = Car;
Enter fullscreen mode Exit fullscreen mode

This is useful in a generic context (see above)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more