Skip to content
BSD
Esc
navigateopen⌘Jpreview
On this page

Unions

Decode alternative layouts and value-dependent schemas.

union(...schemas) attempts at least two schemas in declaration order and returns the first successful result.

Discrimination

When defining schemas to be used inside a union, you can use the literal() schema to brand each branch. This provides proper type-narrowing when using the decoded value inside a switch or another control flow statement.

const User = struct({
  type: literal("user"),
  id: u16(),
});

const Message = struct({
  type: literal("message"),
  text: bytes(u8()).utf8(),
});

const Record = union(User, Message);

const input = Uint8Array.from(...);
const record = Record.decode(input);

switch (record.type) {
    case "user": break;
    case "message": break;
}

Specificity and ordering

Unions are decoded inside a try/catch block. Each branch is decoded in the order they are declared. The union will exit when the first branch succeeds without throwing.

Therefore, the order in which each branch appears matters. If the first branch can never fail to decode, then the first branch will always be matched.

You can increase the specificity of a branch through the use of checks:

const Admin = struct({ type: literal("admin"), id: u16().is(0) });
const User = struct({ type: literal("user"), id: u16() });

// This union will always return a User:
const Record = union(User, Admin);

// This union will correctly discriminate between Admin and User:
const Record = union(Admin, User);

Union vs pipe

Primitive Selection strategy
union(a, b) Trial decode from a shared starting offset
schema.pipe(fn) Deterministic selection from a decoded value
const Record = union(Admin, User);
const Record = bool().pipe((isAdmin) => (isAdmin ? Admin : User));

Was this page helpful?