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;
// }
-
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.
discriminator()
takes two parameters:
- A string representing the tagged union's tag field.
- An object where the keys are the tag values and the values are
object()
,type()
, ordiscriminator()
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;
// }
pnpm build
pnpm test
pnpm release-it