AST Visitor Pattern
Implement the visitor pattern for type-safe discriminated unions with multiple dispatch sites.
Installation
- Make sure Claude is on your device and in your terminal.
Skills load from
~/.claude/skills/when Claude Code starts up β so you need it on your machine first. If you don't have it yet, install it once with the command below, then runclaudein any terminal to verify.One-time setupnpm i -g @anthropic-ai/claude-codeAlready have it? Skip ahead.
- Paste into Claude Code or into your terminal.
This copies the whole skill folder into
~/.claude/skills/ast-visitor-pattern-prisma/β the SKILL.md plus any scripts, reference docs, or templates the skill ships with. Safe default: works for every skill.Faster alternative (instruction-only skills)
Skips the clone and grabs only the SKILL.md file. Don't use this if the skill ships Python scripts, reference markdowns, or asset templates β they won't be downloaded and the skill will fail when it tries to load them.
Quick install (SKILL.md only)Sign up to copy - Restart Claude Code.
Quit and reopen Claude Code (or any other agent that loads from
~/.claude/skills/). New skills are picked up on startup. - Just ask Claude.
Skills auto-activate when your request matches the skill's description β no slash command needed. Trigger phrases live in the skill's own frontmatter; you can read them in the βWhat this skill doesβ section above.
Prefer to read the source first? Open on GitHub.
When Claude uses it
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.
What this skill does
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
MongoAstNodebase βpackages/2-mongo-family/4-query/query-ast/src/ast-node.tsMongoDdlCommandVisitorinterface β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
Related skills
Generative Code Art
anthropics
Create algorithmic art with p5.js using randomness and interactive parameters.
Poster & Visual Design
anthropics
Create original posters and visual art in PNG and PDF formats.
Claude API Helper
anthropics
Build, debug, and optimize Claude API applications with caching and model migration support.
MCP Server Builder
anthropics
Build protocol servers that connect language models to external APIs and services.