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)

Top comments (0)