Schema Unions
Schema unions allow you to create types that can be one of several different schemas, similar to TypeScript union types. They use a discriminator field to determine which specific schema an instance represents at runtime, enabling type-safe polymorphism in your Jazz applications.
The following operations are not available in schema unions:
$jazz.ensureLoaded— use the union schema'sloadmethod, or narrow the type first$jazz.subscribe— use the union schema'ssubscribemethod$jazz.set— use$jazz.applyDiff
Creating schema unions
Schema unions are defined with co.discriminatedUnion() by providing an array of schemas and a discriminator field.
The discriminator field must be a z.literal().
constButtonWidget =const ButtonWidget: co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>import coco.map({map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }>(shape: { type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }): co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group> export maptype: z.z.ZodLiteral<"button">type:import zz.literal("button"),literal<"button">(value: "button", params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<"button"> (+1 overload) export literallabel: z.z.ZodStringlabel:import zz.string(), }); constfunction string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload) export stringSliderWidget =const SliderWidget: co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>import coco.map({map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }>(shape: { type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }): co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group> export maptype: z.z.ZodLiteral<"slider">type:import zz.literal("slider"),literal<"slider">(value: "slider", params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<"slider"> (+1 overload) export literalmin: z.z.ZodNumbermin:import zz.number(),function number(params?: string | z.z.core.$ZodNumberParams): z.z.ZodNumber export numbermax: z.z.ZodNumbermax:import zz.number(), }); constfunction number(params?: string | z.z.core.$ZodNumberParams): z.z.ZodNumber export numberWidget =const Widget: co.DiscriminatedUnion<[co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>]>import coco.discriminatedUnion("type", [discriminatedUnion<[co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>]>(discriminator: string, schemas: [...]): co.DiscriminatedUnion<...> export discriminatedUnionButtonWidget,const ButtonWidget: co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>SliderWidget]); constconst SliderWidget: co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>Dashboard =const Dashboard: co.Map<{ widgets: co.List<co.DiscriminatedUnion<[co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>]>>; }, unknown, Account | Group>import coco.map({map<{ widgets: co.List<co.DiscriminatedUnion<[co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>]>>; }>(shape: { ...; }): co.Map<...> export mapwidgets:widgets: co.List<co.DiscriminatedUnion<[co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>]>>import coco.list(list<co.DiscriminatedUnion<[co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>]>>(element: co.DiscriminatedUnion<...>): co.List<...> export listWidget), });const Widget: co.DiscriminatedUnion<[co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>]>
To instantiate a schema union, just use the create method of one of the member schemas:
constdashboard =const dashboard: { readonly widgets: CoList<({ readonly type: "button"; readonly label: string; } & CoMap) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap)>; } & CoMapDashboard.const Dashboard: co.Map<{ widgets: co.List<co.DiscriminatedUnion<[co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>]>>; }, unknown, Account | Group>create({CoMapSchema<{ widgets: CoListSchema<CoDiscriminatedUnionSchema<[CoMapSchema<{ type: ZodLiteral<"button">; label: ZodString; }, unknown, Account | Group>, CoMapSchema<{ type: ZodLiteral<"slider">; min: ZodNumber; max: ZodNumber; }, unknown, Account | Group>]>>; }, unknown, Account | Group>.create(init: { widgets: CoList<({ readonly type: "button"; readonly label: string; } & CoMap) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap) | null> | readonly (NonNullable<...> | ... 1 more ... | { ...; })[]; }, options?: { owner?: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): { ...; } & CoMap (+1 overload)widgets: [widgets: CoList<({ readonly type: "button"; readonly label: string; } & CoMap) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap) | null> | readonly (NonNullable<...> | ... 1 more ... | { ...; })[]ButtonWidget.const ButtonWidget: co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>create({CoMapSchema<{ type: ZodLiteral<"button">; label: ZodString; }, unknown, Account | Group>.create(init: { type: "button"; label: string; }, options?: { owner?: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): { ...; } & CoMap (+1 overload)type: "button"type: "button",label: stringlabel: "Click me" }),SliderWidget.const SliderWidget: co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>create({CoMapSchema<{ type: ZodLiteral<"slider">; min: ZodNumber; max: ZodNumber; }, unknown, Account | Group>.create(init: { type: "slider"; min: number; max: number; }, options?: { owner?: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): { ...; } & CoMap (+1 overload)type: "slider"type: "slider",min: numbermin: 0,max: numbermax: 100 }) ] });
You can also use plain JSON objects, and let Jazz infer the concrete type from the discriminator field:
constdashboard =const dashboard: { readonly widgets: CoList<({ readonly type: "button"; readonly label: string; } & CoMap) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap)>; } & CoMapDashboard.const Dashboard: co.Map<{ widgets: co.List<co.DiscriminatedUnion<[co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>]>>; }, unknown, Account | Group>create({CoMapSchema<{ widgets: CoListSchema<CoDiscriminatedUnionSchema<[CoMapSchema<{ type: ZodLiteral<"button">; label: ZodString; }, unknown, Account | Group>, CoMapSchema<{ type: ZodLiteral<"slider">; min: ZodNumber; max: ZodNumber; }, unknown, Account | Group>]>>; }, unknown, Account | Group>.create(init: { widgets: CoList<({ readonly type: "button"; readonly label: string; } & CoMap) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap) | null> | readonly (NonNullable<...> | ... 1 more ... | { ...; })[]; }, options?: { owner?: Group; unique?: CoValueUniqueness["uniqueness"]; } | Group): { ...; } & CoMap (+1 overload)widgets: [ {widgets: CoList<({ readonly type: "button"; readonly label: string; } & CoMap) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap) | null> | readonly (NonNullable<...> | ... 1 more ... | { ...; })[]type: "button"type: "button",label: stringlabel: "Click me" }, {type: "slider"type: "slider",min: numbermin: 0,max: numbermax: 100 } ] });
Narrowing unions
When working with schema unions, you can access any property that is common to all members of the union. To access properties specific to a particular union member, you need to narrow the type. You can do this using a TypeScript type guard on the discriminator field:
dashboard.const dashboard: { readonly widgets: CoList<({ readonly type: "button"; readonly label: string; } & CoMap) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap)>; } & CoMapwidgets.widgets: CoList<({ readonly type: "button"; readonly label: string; } & CoMap) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap)>Array<({ readonly type: "button"; readonly label: string; } & CoMap) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap)>.forEach(callbackfn: (value: ({ readonly type: "button"; readonly label: string; } & CoMap) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap), index: number, array: (({ readonly type: "button"; readonly label: string; } & CoMap) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap))[]) => void, thisArg?: any): voidPerforms the specified action for each element in an array.forEach((widget) => { if (widget: ({ readonly type: "button"; readonly label: string; } & CoMap) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap)widget.widget: ({ readonly type: "button"; readonly label: string; } & CoMap) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap)type: "button" | "slider"type === "button") {var console: ConsoleThe `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```console.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(`Button: ${widget.widget: { readonly type: "button"; readonly label: string; } & CoMaplabel: stringlabel}`); } else if (widget.widget: { readonly type: "slider"; readonly min: number; readonly max: number; } & CoMaptype: "slider"type === "slider") {var console: ConsoleThe `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```console.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log(`Slider: ${widget.widget: { readonly type: "slider"; readonly min: number; readonly max: number; } & CoMapmin: numbermin} to ${widget.widget: { readonly type: "slider"; readonly min: number; readonly max: number; } & CoMapmax: numbermax}`); } });
Loading schema unions
You can load an instance of a schema union using its ID, without having to know its concrete type:
// Load a widget by ID constwidget = awaitconst widget: ({ readonly type: "button"; readonly label: string; } & CoMap & SchemaUnion) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap & SchemaUnion) | nullWidget.const Widget: co.DiscriminatedUnion<[co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>]>load(CoDiscriminatedUnionSchema<[CoMapSchema<{ type: ZodLiteral<"button">; label: ZodString; }, unknown, Account | Group>, CoMapSchema<{ type: ZodLiteral<"slider">; min: ZodNumber; max: ZodNumber; }, unknown, Account | Group>]>.load(id: string, options?: { loadAs?: Account | AnonymousJazzAgent; skipRetry?: boolean; unstable_branch?: BranchDefinition; }): Promise<...>const widgetId: "widgetId"widgetId); // Subscribe to updates constconst unsubscribe: () => voidunsubscribe =Widget.const Widget: co.DiscriminatedUnion<[co.Map<{ type: z.z.ZodLiteral<"button">; label: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ type: z.z.ZodLiteral<"slider">; min: z.z.ZodNumber; max: z.z.ZodNumber; }, unknown, Account | Group>]>subscribe(CoDiscriminatedUnionSchema<[CoMapSchema<{ type: ZodLiteral<"button">; label: ZodString; }, unknown, Account | Group>, CoMapSchema<{ type: ZodLiteral<"slider">; min: ZodNumber; max: ZodNumber; }, unknown, Account | Group>]>.subscribe(id: string, options: SubscribeListenerOptions<({ readonly type: "button"; readonly label: string; } & CoMap & SchemaUnion) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap & SchemaUnion), true>, listener: (value: ({ readonly type: "button"; readonly label: string; } & ... 1 more ... & SchemaUnion) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & ... 1 more ... & SchemaUnion), unsubscribe: () => void) => void): () => voidconst widgetId: "widgetId"widgetId, {}, (widget) => {widget: ({ readonly type: "button"; readonly label: string; } & CoMap & SchemaUnion) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap & SchemaUnion)var console: ConsoleThe `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```console.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Widget updated:",widget); });widget: ({ readonly type: "button"; readonly label: string; } & CoMap & SchemaUnion) | ({ readonly type: "slider"; readonly min: number; readonly max: number; } & CoMap & SchemaUnion)
Resolve queries are not supported in schema unions yet. If you need to deeply load a schema union,
you'll need to first shallowly load the union, and then load the nested properties after narrowing the union type (using $jazz.ensureLoaded or useCoState).
Nested schema unions
You can create complex hierarchies by nesting discriminated unions within other unions:
// Define error types constBadRequestError =const BadRequestError: co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<400>; message: z.z.ZodString; }, unknown, Account | Group>import coco.map({map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<400>; message: z.z.ZodString; }>(shape: { status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<400>; message: z.z.ZodString; }): co.Map<...> export mapstatus: z.z.ZodLiteral<"failed">status:import zz.literal("failed"),literal<"failed">(value: "failed", params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<"failed"> (+1 overload) export literalcode: z.z.ZodLiteral<400>code:import zz.literal(400),literal<400>(value: 400, params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<400> (+1 overload) export literalmessage: z.z.ZodStringmessage:import zz.string(), }); constfunction string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload) export stringUnauthorizedError =const UnauthorizedError: co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<401>; message: z.z.ZodString; }, unknown, Account | Group>import coco.map({map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<401>; message: z.z.ZodString; }>(shape: { status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<401>; message: z.z.ZodString; }): co.Map<...> export mapstatus: z.z.ZodLiteral<"failed">status:import zz.literal("failed"),literal<"failed">(value: "failed", params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<"failed"> (+1 overload) export literalcode: z.z.ZodLiteral<401>code:import zz.literal(401),literal<401>(value: 401, params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<401> (+1 overload) export literalmessage: z.z.ZodStringmessage:import zz.string(), }); constfunction string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload) export stringInternalServerError =const InternalServerError: co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<500>; message: z.z.ZodString; }, unknown, Account | Group>import coco.map({map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<500>; message: z.z.ZodString; }>(shape: { status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<500>; message: z.z.ZodString; }): co.Map<...> export mapstatus: z.z.ZodLiteral<"failed">status:import zz.literal("failed"),literal<"failed">(value: "failed", params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<"failed"> (+1 overload) export literalcode: z.z.ZodLiteral<500>code:import zz.literal(500),literal<500>(value: 500, params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<500> (+1 overload) export literalmessage: z.z.ZodStringmessage:import zz.string(), }); // Create a union of error types constfunction string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload) export stringErrorResponse =const ErrorResponse: co.DiscriminatedUnion<[co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<400>; message: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<401>; message: z.z.ZodString; }, unknown, Account | Group>, co.Map<...>]>import coco.discriminatedUnion("code", [discriminatedUnion<[co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<400>; message: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<...>; message: z.z.ZodString; }, unknown, Account | Group>, co.Map<...>]>(discriminator: string, schemas: [...]): co.DiscriminatedUnion<...> export discriminatedUnionBadRequestError,const BadRequestError: co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<400>; message: z.z.ZodString; }, unknown, Account | Group>UnauthorizedError,const UnauthorizedError: co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<401>; message: z.z.ZodString; }, unknown, Account | Group>InternalServerError, ]); // Define success type constconst InternalServerError: co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<500>; message: z.z.ZodString; }, unknown, Account | Group>SuccessResponse =const SuccessResponse: co.Map<{ status: z.z.ZodLiteral<"success">; data: z.z.ZodString; }, unknown, Account | Group>import coco.map({map<{ status: z.z.ZodLiteral<"success">; data: z.z.ZodString; }>(shape: { status: z.z.ZodLiteral<"success">; data: z.z.ZodString; }): co.Map<{ status: z.z.ZodLiteral<"success">; data: z.z.ZodString; }, unknown, Account | Group> export mapstatus: z.z.ZodLiteral<"success">status:import zz.literal("success"),literal<"success">(value: "success", params?: string | z.z.core.$ZodLiteralParams): z.z.ZodLiteral<"success"> (+1 overload) export literaldata: z.z.ZodStringdata:import zz.string(), }); // Create a top-level union that includes the error union constfunction string(params?: string | z.z.core.$ZodStringParams): z.z.ZodString (+1 overload) export stringApiResponse =const ApiResponse: co.DiscriminatedUnion<[co.Map<{ status: z.z.ZodLiteral<"success">; data: z.z.ZodString; }, unknown, Account | Group>, co.DiscriminatedUnion<[co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<400>; message: z.z.ZodString; }, unknown, Account | Group>, co.Map<...>, co.Map<...>]>]>import coco.discriminatedUnion("status", [discriminatedUnion<[co.Map<{ status: z.z.ZodLiteral<"success">; data: z.z.ZodString; }, unknown, Account | Group>, co.DiscriminatedUnion<[co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<400>; message: z.z.ZodString; }, unknown, Account | Group>, co.Map<...>, co.Map<...>]>]>(discriminator: string, schemas: [...]): co.DiscriminatedUnion<...> export discriminatedUnionSuccessResponse,const SuccessResponse: co.Map<{ status: z.z.ZodLiteral<"success">; data: z.z.ZodString; }, unknown, Account | Group>ErrorResponse, ]); functionconst ErrorResponse: co.DiscriminatedUnion<[co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<400>; message: z.z.ZodString; }, unknown, Account | Group>, co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<401>; message: z.z.ZodString; }, unknown, Account | Group>, co.Map<...>]>function handleResponse(response: co.loaded<typeof ApiResponse>): voidhandleResponse(response:response: NonNullable<({ readonly status: "failed"; readonly code: 400; readonly message: string; } & CoMap) | ({ readonly status: "failed"; readonly code: 401; readonly message: string; } & CoMap) | ({ ...; } & CoMap) | ({ ...; } & CoMap) | null>import coco.loaded<typeoftype loaded<T extends CoValueClassOrSchema, R extends ResolveQuery<T> = true> = R extends boolean | undefined ? NonNullable<InstanceOfSchemaCoValuesNullable<T>> : [NonNullable<InstanceOfSchemaCoValuesNullable<T>>] extends [...] ? Exclude<...> extends CoValue ? R extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean | undefined ? CoValue & Exclude<...> : [...] extends [...] ? Exclude<...> extends CoValue ? ItemDepth extends { ...; } ? readonly ((CoValue & ... 1 more ... & (ItemDepth extends boolean ... export loadedApiResponse>) { if (const ApiResponse: co.DiscriminatedUnion<[co.Map<{ status: z.z.ZodLiteral<"success">; data: z.z.ZodString; }, unknown, Account | Group>, co.DiscriminatedUnion<[co.Map<{ status: z.z.ZodLiteral<"failed">; code: z.z.ZodLiteral<400>; message: z.z.ZodString; }, unknown, Account | Group>, co.Map<...>, co.Map<...>]>]>response.response: NonNullable<({ readonly status: "failed"; readonly code: 400; readonly message: string; } & CoMap) | ({ readonly status: "failed"; readonly code: 401; readonly message: string; } & CoMap) | ({ ...; } & CoMap) | ({ ...; } & CoMap) | null>status: "failed" | "success"status === "success") {var console: ConsoleThe `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```console.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Success:",response.response: { ...; } & CoMapdata: stringdata); } else { // This is an error - narrow further by error code if (response.response: ({ readonly status: "failed"; readonly code: 400; readonly message: string; } & CoMap) | ({ readonly status: "failed"; readonly code: 401; readonly message: string; } & CoMap) | ({ ...; } & CoMap)code: 400 | 401 | 500code === 400) {var console: ConsoleThe `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```console.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Bad request:",response.response: { readonly status: "failed"; readonly code: 400; readonly message: string; } & CoMapmessage: stringmessage); } else if (response.response: ({ readonly status: "failed"; readonly code: 401; readonly message: string; } & CoMap) | ({ ...; } & CoMap)code: 401 | 500code === 401) {var console: ConsoleThe `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```console.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Unauthorized:",response.response: { readonly status: "failed"; readonly code: 401; readonly message: string; } & CoMapmessage: stringmessage); } else if (response.response: { ...; } & CoMapcode: 500code === 500) {var console: ConsoleThe `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```console.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)log("Server error:",response.response: { ...; } & CoMapmessage: stringmessage); } } }
Limitations with schema unions
Schema unions have some limitations that you should be aware of. They are due to TypeScript behaviour with type unions: when the type members of the union have methods with generic parameters, TypeScript will not allow calling those methods on the union type. This affects some of the methods on the $jazz namespace.
Note that these methods may still work at runtime, but their use is not recommended as you will lose type safety.
$jazz.ensureLoaded and $jazz.subscribe not supported
The $jazz.ensureLoaded and $jazz.subscribe methods are not available in schema unions.
Instead, use the union schema's load and subscribe methods.
Updating union fields
You can't use $jazz.set to modify a schema union's fields (even if the field is present in all the union members).
Use $jazz.applyDiff instead.