ast-visitor-pattern

作成者: prisma

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,…

npx skills add https://github.com/prisma/prisma-next --skill ast-visitor-pattern

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, always in the same file:

// 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 / MongoDdlCommandVisitorpackages/2-mongo-family/4-query/query-ast/src/ddl-commands.ts
  • OpFactoryCall / OpFactoryCallVisitorpackages/3-mongo-target/1-mongo-target/src/core/op-factory-call.ts

prismaのその他のスキル

prisma-cli-migrate-reset
prisma
prisma migrate reset
official
prisma-cli-validate
prisma
prismaのバリデーション。このPrisma機能を使用する際の参考情報。
official
prisma-next-extension-upgrade
prisma
Upgrade Prisma Next in your extension. Bumps every `@prisma-next/*` dependency to the requested target (or npm `latest`), runs the per-transition upgrade…
official
adr-review
prisma
1つ以上のADRを新たな視点で(事前知識のないチームメンバーとして)レビューし、ナラティブや構造上の問題を特定した上で、それらを書き直します。以下の場合に使用します…
official
prisma-next-upgrade
prisma
Upgrade Prisma Next in your app. Bumps every `@prisma-next/*` dependency from the version pinned in the lockfile to the requested target (or npm `latest`),…
official
prisma-cli
prisma
Prisma CLIコマンド、オプション、およびセットアップ、マイグレーション、データベース操作にわたるワークフローの完全なリファレンス。優先度別に整理された20以上のコマンドをカバー:セットアップ(init)、生成(generate)、開発(dev)、データベース操作(db pull/push/seed/execute)、マイグレーション(migrate dev/deploy/reset/status/diff/resolve)。Prisma 7.xの変更点を含む:新しいprisma.config.ts設定ファイル、削除されたフラグ(--skip-generate、--skip-seed、--schema、--url)、および明示的な...
official
prisma-client-api
prisma
Prisma Client APIの完全なリファレンス。モデルクエリ、CRUD操作、フィルタリング、リレーション、トランザクションに対応。findUnique、findMany、create、update、delete、upsert、バルク操作とその戻り値バリアントを含む17のモデルクエリメソッドを網羅。結果を整形するためのクエリオプション(select、include、omit、orderBy、take、skip、cursor、distinct)を提供。スカラーおよび論理フィルター演算子(equals、in、contains、startsWith、lt、gt)に加え、リレーションフィルター(some、...)を含む。
official
prisma-compute
prisma
Prisma Compute deployment and hosting guide. Use whenever the user mentions Prisma Compute, `prisma.compute.ts`, `defineComputeConfig`, deploying or hosting a Prisma app, `@prisma/cli app deploy`, `compute:deploy`, `create-prisma --deploy`, `PRISMA_SERVICE_TOKEN`, `auth workspace`, Compute apps/deployments/build logs/domains, `@prisma/cli agent install`, localhost vs `0.0.0.0`, deploy port binding, or framework deploy readiness for Hono, Elysia, Next.js, TanStack Start, Astro, Nuxt, Svelte,...
developmentdevopsofficial