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

0.3.1 • 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

pnpm build

Testing

pnpm test

Releasing

pnpm release-it

/@birchill/discriminator/

    Package Sidebar

    Install

    npm i @birchill/discriminator

    Weekly Downloads

    376

    Version

    0.3.1

    License

    MIT

    Unpacked Size

    24.9 kB

    Total Files

    9

    Last publish

    Collaborators

    • birtles