Skip to content

Unions and polymorphism

Typra supports closed string unions, open string unions, and discriminated model hierarchies.

@parseAlias("ready", #["complete"])
union FixtureStatus {
draft: "draft";
ready: "ready";
archived: "archived";
}

@parseAlias is parse-only. Loading accepts complete, but saving emits the canonical TypeSpec value ready.

@parseAlias("batch", #["bulk"])
union FixtureMode {
interactive: "interactive";
batch: "batch";
custom: string;
}

Open unions preserve known values while allowing target-specific handling for custom strings.

@discriminator("kind")
model FixtureContent {
kind: string;
}
model TextContent extends FixtureContent {
kind: "text";
text: string;
}
model ImageContent extends FixtureContent {
kind: "image";
url: string;
}

Typra uses the discriminator field to load and save the right child shape. In the fixture contract, FixtureRoot.content and FixtureRoot.contentItems exercise both a single polymorphic value and a polymorphic collection.

export type FixtureMode = "interactive" | "batch" | (string & {});
export type FixtureStatus = "draft" | "ready" | "archived";
function parseFixtureStatus(value: unknown): FixtureStatus {
switch (String(value)) {
case "complete":
return "ready";
default:
throw new Error(`Invalid FixtureStatus value: ${value}`);
}
}
private static loadKind(data: Record<string, unknown>, context?: LoadContext): FixtureContent {
switch (String(data["kind"]).toLowerCase()) {
case "text":
return TextContent.load(data, context);
case "image":
return ImageContent.load(data, context);
default:
return new FixtureContent();
}
}