@birchill/discriminator
TypeScript icon, indicating that this package has built-in type declarations

0.3.0 • Public • Published

discriminator

A tagged union type for superstruct based on JSON typedef's discriminator type.

import { boolean, Infer, enums, string, object } from 'superstruct';
import { discriminator } from '@birchill/discriminator';

const schema = discriminator('eventType', {
  USER_CREATED: object({ id: string() }),
  USER_PAYMENT_PLAN_CHANGED: object({
    id: string(),
    plan: enums(['FREE', 'PAID']),
  }),
  USER_DELETED: object({ id: string(), softDelete: boolean() }),
});

type SchemaType = Infer<typeof schema>;

// SchemaType =
// {
//   eventType: "USER_CREATED";
//   id: string;
// } | {
//   eventType: "USER_PAYMENT_PLAN_CHANGED";
//   id: string;
//   plan: "FREE" | "PAID";
// } | {
//   eventType: "USER_DELETED";
//   id: string;
//   softDelete: boolean;
// }

Why?

  • If you try to model the above using union() objects and validation fails you get errors like:

    Expected the value to satisfy a union of ``object | object | object``, but received: [object Object].

    Using discriminator() you get errors like:

    At path: value.name -- Expected a string with a length between ``0``and ``256`` but received one with a length of ``257`` .

  • Better semantics.

  • Easier translation to and from JSON typedef should that be useful.

Specifics

discriminator() takes two parameters:

  1. A string representing the tagged union's tag field.
  2. An object where the keys are the tag values and the values are object(), type(), or discriminator() structs.

If you need to model a branch where there are no other properties just use an empty object() or type(). This is important because you're indicating whether or not that branch is allowed to have extra values on it (type()) or not (object()).

e.g.

discriminator('action', {
  signin: type({ email: string(), token: string() }),
  signout: type(),
});

You can nest discriminator() objects like so:

discriminator('result', {
  success: discriminator('task', {
    upload: type({
      filename: string(),
    }),
    download: type({
      filename: string(),
      bytes: number(),
    }),
  }),
  failure: type({
    code: number(),
  }),
});

// `Infer` here produces the type:
//
// {
//   result: "success";
//   task: "upload";
//   filename: string;
// } | {
//   result: "success";
//   task: "download";
//   filename: string;
//   bytes: number;
// } | {
//   result: "failure";
//   code: number;
// }

Developing

Building

yarn build

Testing

yarn test

Releasing

yarn release-it

Package Sidebar

Install

npm i @birchill/discriminator

Weekly Downloads

136

Version

0.3.0

License

MIT

Unpacked Size

24.9 kB

Total Files

9

Last publish

Collaborators

  • birtles