Skip to content
cccskills
FreePublished on GitHub

ast-visitor-pattern

Refactor TypeScript code that handles several variants in multiple places using frozen classes and a Visitor interface, which defines a handler for each variant. This helps type checking expose missing handlers when new variants are added.

OriginalShow the original description

Use the frozen-class/visitor pattern for discriminated unions that have multiple dispatch sites. Use when creating a new set of variants (commands, IR nodes, factory calls) that will be switched over in 2+ places, or when refactoring an existing union type that has grown multiple switch sites.

See how to install

Good for

  • Refactor a union type with repeated switch statements
  • Design commands or nodes that need several kinds of processing
  • Catch missing handlers when adding a new variant

Files included (1)

  • SKILL.md3.3 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

AST Class/Visitor Pattern

When a discriminated union has 3+ variants and 2+ dispatch sites (renderers, serializers, classifiers, etc.), replace plain union + switch with frozen subclasses and a visitor interface. This makes adding a new variant a compiler error at every consumer, instead of a silent omission.

Structure

Four pieces, usually in one file — the Mongo DDL set below spreads them over three:

// 1. Abstract base (not exported — consumers use the union type)
abstract class FooNode {
  abstract readonly kind: string;
  abstract accept<R>(visitor: FooVisitor<R>): R;
  protected freeze(): void { Object.freeze(this); }
}

// 2. Visitor interface
export interface FooVisitor<R> {
  bar(node: BarNode): R;
  baz(node: BazNode): R;
}

// 3. Concrete subclasses — readonly fields, freeze() in constructor
export class BarNode extends FooNode {
  readonly kind = 'bar' as const;
  readonly value: string;
  constructor(value: string) {
    super();
    this.value = value;
    this.freeze();
  }
  accept<R>(visitor: FooVisitor<R>): R { return visitor.bar(this); }
}

export class BazNode extends FooNode {
  readonly kind = 'baz' as const;
  readonly count: number;
  constructor(count: number) {
    super();
    this.count = count;
    this.freeze();
  }
  accept<R>(visitor: FooVisitor<R>): R { return visitor.baz(this); }
}

// 4. Union type
export type Foo = BarNode | BazNode;

Consuming

Define a visitor object (or class) per concern:

const renderVisitor: FooVisitor<string> = {
  bar(node) { return node.value; },
  baz(node) { return String(node.count); },
};

function render(node: Foo): string {
  return node.accept(renderVisitor);
}

Always construct instances, never frozen object literals

This holds everywhere a node is built — tests and production construction surfaces (contract-free factories, builders). A factory must return new BarNode(...), never Object.freeze({ kind: 'bar', value: 'x' }). A frozen plain object has no prototype, so instanceof fails, accept() is missing, and a downstream shallow-copy ({ ...node }) silently strips the type back to an anonymous bag; constructor-time invariants are skipped too.

// ✅
const call = new BarNode('x');
export function bar(value: string): BarNode { return new BarNode(value); }

// ❌
const call: Foo = { kind: 'bar', value: 'x' };
export function bar(value: string): Foo { return Object.freeze({ kind: 'bar', value }); }

When NOT to use

  • Single dispatch site → plain union + switch is simpler
  • Fewer than 3 variants with no expected growth → not worth the boilerplate

Codebase examples

  • MongoAstNode base — packages/2-mongo-family/4-query/query-ast/src/ast-node.ts
  • MongoDdlCommandVisitor interface — packages/2-mongo-family/4-query/query-ast/src/ddl-visitors.ts
  • Concrete DDL commands and their union — packages/2-mongo-family/4-query/query-ast/src/ddl-commands.ts

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

adr-review

FreeSummary

Review and rewrite architecture decision records, documents that explain design choices, from a newcomer’s perspective. Identify missing context, buried decisions, and temporary details, then reorganize each document so future readers can follow the reasoning.

  • Reviewing design decisions from a new team member’s perspective
  • Making an ADR’s decision and reasoning easier to follow
  • Removing temporary project context from long-lived records
prisma/orm47.7KUpdated Oct 17, 2026

bumping-biome

FreeSummary

Update Biome packages with pnpm, align configuration filenames and schema versions across the repository, and run the configured checks and fixes. Receive a report of remaining issues, with optional further fixes subject to agreement.

  • Updating Biome to a specified version
  • Aligning configuration files after dependency updates
  • Checking for issues introduced by a Biome update
prisma/orm47.7KUpdated Oct 17, 2026

contrib-pr

FreeSummary

Prepare and open a pull request for an external contribution to prisma/orm. Check contribution rules, change scope, tests, and contributor signoffs, then draft the title and explanation for approval before submitting from a fork.

  • Submitting a Prisma contribution from a fork
  • Checking tests and scope before requesting review
  • Verifying contributor signoffs on every commit
prisma/orm47.7KUpdated Oct 17, 2026

create-pr

FreeSummary

Create GitHub pull requests for Prisma 8 with Linear ticket IDs in their titles. Turn branch changes into a clear description covering concrete examples, design decisions, behavior changes, and testing evidence for reviewers.

  • Submitting branch changes for review
  • Linking a pull request to a Linear ticket
  • Explaining design decisions and behavior changes
prisma/orm47.7KUpdated Oct 17, 2026

Draft Prisma 8 release notes from merged PRs since the previous published release. Select user-facing changes, put breaking changes first, link migration guidance and PR evidence, and mirror the notes in CHANGELOG.md.

  • Draft notes for a stable or RC release
  • Summarize changes since the previous published release
  • Explain breaking changes with migration guidance
prisma/orm47.7KUpdated Oct 17, 2026

Coordinate GitHub pull request reviews by assigning comment triage and code changes to dedicated sub-agents. Repeat the process, track decisions and progress, and reply to and resolve threads as fixes are completed.

  • Work through outstanding pull request review comments
  • Separate required fixes from items outside the pull request scope
  • Repeat review and implementation until actionable items are cleared
prisma/orm47.7KUpdated Oct 17, 2026

See all skills by prisma

Report a problem with this skill