TypeScript · Node.js

Claude Code for TypeScript & Node.js Developers: The Definitive Guide (2026)

CLAUDE.md templates, debugging patterns, type inference workflows, and 20 copy-paste prompts for NestJS, Express, Next.js, and Prisma.

Contents

  1. Why TypeScript + Claude Code is a force multiplier
  2. CLAUDE.md template for TypeScript/Node.js projects
  3. Type error debugging patterns
  4. Framework-specific workflows (NestJS, Next.js, Express)
  5. Prisma & database workflows
  6. Testing with Vitest and Jest
  7. 20 high-value TypeScript prompts
  8. Common mistakes TypeScript devs make with Claude Code

TypeScript is one of the best languages to pair with Claude Code. The type system gives Claude rich context about your codebase — interfaces, generics, enums, and module structure all signal intent that Claude can use to generate more accurate, idiomatic code.

But most developers use Claude Code the same way they'd use a generic chatbot: paste an error, ask for a fix. This guide shows a better approach — one that uses TypeScript's own structure to make Claude dramatically more useful.

Why TypeScript + Claude Code is a force multiplier

When you work in JavaScript, Claude is guessing at types. When you work in TypeScript, Claude can read your type system and generate code that fits it correctly the first time.

ScenarioJavaScriptTypeScript
Generate a functionClaude guesses arg types, often wrongClaude reads interfaces, types correctly on first try
Refactor a moduleEasy to break implicit contractsClaude sees explicit contracts via types
Add a new endpointClaude invents its own structureClaude matches existing DTOs and response schemas
Debug an errorRuntime errors onlyClaude can fix type errors before they reach runtime

The practical result: fewer iteration cycles. When Claude understands your types, the first draft is closer to correct.

CLAUDE.md template for TypeScript/Node.js projects

This template gives Claude the context it needs to generate correct TypeScript code from the start. Customize the sections for your stack.

# CLAUDE.md — TypeScript / Node.js Project

## Language & Runtime
- TypeScript 5.x — strict mode enabled
- Node.js 22 (LTS)
- Package manager: pnpm (lockfile: pnpm-lock.yaml — never delete it)
- Module system: ESM (use import/export, not require/module.exports)

## Architecture
- src/           → application source
- src/modules/   → feature modules (NestJS style, or domain folders for Express)
- src/types/     → shared interfaces and type definitions
- src/lib/       → pure utility functions (no side effects, fully tested)
- src/config/    → environment config and validation (use Zod)
- dist/          → compiled output (never edit manually)

## TypeScript Rules
- Strict mode is ON. Never use `any` — use `unknown` and narrow it.
- Prefer `interface` for public shapes; use `type` for unions, intersections, mapped types.
- Generics should have descriptive names: T → TEntity, TResult, TPayload.
- Return types must be explicit on all exported functions.
- Null handling: use `??` not `|| ` for falsy defaults. Always handle `undefined` explicitly.

## Naming Conventions
- Files: kebab-case (user-service.ts, auth.controller.ts)
- Classes: PascalCase
- Functions/variables: camelCase
- Constants: UPPER_SNAKE_CASE
- Types/Interfaces: PascalCase with no 'I' prefix (User, not IUser)

## Testing
- Test framework: Vitest (Jest-compatible API)
- Test files colocated: user.service.spec.ts next to user.service.ts
- Coverage target: 80% for src/lib, 60% for src/modules
- Mock pattern: use vi.mock() at module level, vi.spyOn() for method stubs
- Never mock the thing you're testing

## Error Handling
- Use typed error classes: class AppError extends Error { constructor(public code: string, ...) }
- Async functions must have try/catch or use a Result type
- Never swallow errors silently — log + rethrow or return Result.err()
- HTTP errors: use consistent shape { code, message, statusCode }

## Environment
- All env vars validated at startup via Zod schema in src/config/env.ts
- Never read process.env directly in business logic — import from config
- .env.example is the source of truth for required vars

## Commit Standards
- feat: new feature (triggers minor version bump)
- fix: bug fix (triggers patch)
- chore: tooling, deps (no version bump)
- BREAKING CHANGE: footer triggers major bump

## Do NOT
- Add barrel files (index.ts re-exports) unless I ask
- Use default exports — always named exports
- Import from dist/ — always import from src/
- Generate any: use proper generics or unknown
- Write synchronous file I/O in request handlers
Pro tip: The "Do NOT" section is the most valuable part of your CLAUDE.md. Claude will happily generate default exports or barrel files unless you explicitly forbid it. List your team's top 5 style violations.

Type error debugging patterns

TypeScript errors have a structure Claude can work with. The key is giving Claude the full error, not just the message.

Pattern 1: Full error chain prompt

Don't copy just the error message. Copy the full output including the type trace:

Copy-paste prompt
I'm getting this TypeScript error. Here is the full error output including the type chain: [paste full tsc output here] The relevant file is [filename]. The type I'm trying to satisfy is defined in [types file]. Fix the type error without using `any`. If there are multiple valid approaches, show me the two best with tradeoffs.

Pattern 2: Generic constraint errors

Generic constraint errors are notoriously cryptic. Claude handles them well if you explain what you're trying to accomplish:

Copy-paste prompt
I have this generic function that's throwing a constraint error: [paste function + error] What I'm trying to accomplish: [one sentence description]. Rewrite the generic constraint so it works correctly, and explain why the original constraint was wrong.

Pattern 3: `strictNullChecks` errors

Copy-paste prompt
This code worked before I enabled strictNullChecks. Here are all the new errors: [paste errors] Do not use the non-null assertion operator (!). Fix each error with proper null handling. Where values can legitimately be undefined, make that explicit in the type.

Framework-specific workflows

NestJS

NestJS's decorator-heavy structure is where Claude Code excels. A well-written CLAUDE.md tells Claude your module pattern and it will generate consistent structure every time.

## NestJS conventions (add to your CLAUDE.md)
- Module structure: feature.module.ts, feature.controller.ts, feature.service.ts, feature.dto.ts
- DTOs use class-validator decorators — always @IsString(), @IsNotEmpty(), etc.
- Services inject dependencies via constructor — never use moduleRef.get()
- Response serialization via ClassSerializerInterceptor (add @Exclude() to sensitive fields)
- Guards go in auth/ module and are applied at controller level with @UseGuards()
- Never put business logic in controllers — controllers are thin HTTP adapters only
NestJS module generation prompt
Create a complete NestJS module for [feature name]. Requirements: - CRUD endpoints (POST, GET /:id, GET, PATCH /:id, DELETE /:id) - DTOs for create and update with class-validator - Service with injected [dependency] - Swagger decorators (@ApiTags, @ApiOperation, @ApiResponse) - Unit tests for the service methods Follow the module structure in CLAUDE.md exactly.

Next.js (App Router)

With Next.js App Router, the boundary between server and client components is where type errors most often appear. Be explicit:

Next.js App Router prompt
I'm building a [feature] in Next.js 15 App Router. Server component requirements: [data fetching, auth check, etc.] Client component requirements: [interactivity, state, etc.] Create the component structure with clear server/client boundaries. Use 'use client' only where necessary. Type all props explicitly. Use the fetch API with Next.js caching options where appropriate (no third-party data fetching libraries).

Express / Fastify

## Express conventions (add to your CLAUDE.md)
- Router files in src/routes/ — one file per resource
- Middleware in src/middleware/ — typed as RequestHandler
- All request bodies validated with Zod before touching business logic
- Async route handlers wrapped in asyncHandler() to catch promise rejections
- Error middleware at the bottom of app.ts — never inline try/catch in routes
- Request types extended: interface AuthRequest extends Request { user: JWTPayload }

Prisma & database workflows

Prisma's generated types are a goldmine for Claude. Give Claude access to your schema and it can write type-safe queries that match your actual data model.

Prisma query generation prompt
Using the Prisma schema in prisma/schema.prisma, write a repository method that: [describe the query — what data, what filters, what relations to include] Requirements: - Return type must be explicitly typed using Prisma.XxxGetPayload - Use Prisma's select/include correctly (not both) - Handle the case where the record doesn't exist (return null, don't throw) - Add an appropriate index recommendation as a comment if the query would benefit from one
Key technique: Ask Claude to return Prisma.XxxGetPayload<typeof query> types instead of generic Xxx types. This ensures the return type exactly matches what you're actually selecting, which prevents runtime surprises when you add or remove fields.

Prisma migration workflow

Schema change review prompt
I'm adding [describe schema change] to my Prisma schema. Before I run the migration: 1. Identify any breaking changes this introduces to existing queries 2. Find all files in src/ that use the affected model and need to be updated 3. Check if any existing data might violate new constraints 4. Suggest the migration name (descriptive, snake_case) Here is the current schema: [paste or reference schema.prisma]

Testing with Vitest and Jest

Claude Code writes mediocre tests when given vague instructions. It writes excellent tests when you specify what to test and how to structure mocks.

Test generation prompt (specific)
Write unit tests for [file path]. Test framework: Vitest with @testing-library where applicable. Mock strategy: - [dependency 1]: mock at module level with vi.mock() - [dependency 2]: spy on specific methods with vi.spyOn() - Database: use the mock factory in tests/__mocks__/prisma.ts Cover these scenarios: 1. Happy path 2. [edge case 1] 3. [edge case 2] 4. Input validation failures 5. Dependency throws an error Use describe/it pattern. Each test should test ONE thing. No assertions in beforeEach.
Testing anti-pattern to avoid: Don't ask Claude to "write tests for this file" without specifying mocks. Claude will often import the real dependencies, run actual I/O, and write tests that break in CI. Always specify your mock strategy.

20 high-value TypeScript prompts

Type system

  1. Strict unknown narrowing: "This function receives an unknown value from [source]. Write a type guard that narrows it to [TargetType] using property checks, not type assertions."
  2. Mapped type builder: "Create a mapped type that takes [InputType] and produces a version where [transformation]. Show how to use it with an example."
  3. Generic utility: "I need a generic function that [behavior]. It should work with [TypeA], [TypeB], and any type that satisfies [constraint]. Don't use any."
  4. Discriminated union handler: "I have this discriminated union. Write an exhaustive switch statement that handles all variants. Make it fail at compile time if a new variant is added without a handler."
  5. Template literal types: "Generate a template literal type that [describes the pattern, e.g., 'validates event names follow `on${EventName}` format']."

Refactoring

  1. Async cleanup: "This function has nested promise chains. Rewrite it using async/await with proper error handling. Keep the same TypeScript types and don't change behavior."
  2. Extract service: "This component has too much business logic. Extract it into a service class that can be unit tested independently. The component should only call the service."
  3. Remove any: "Find every use of `any` in this file and replace with proper types. For each change, explain what type you used and why."
  4. Enum to union: "Convert this enum to a const object + string union. Show the before and after, and update the 3 files that use it."
  5. Error handling overhaul: "Replace all thrown errors in this module with a Result type. Use [neverthrow/your own Result type]. Callers should be forced to handle errors — don't let them ignore them."

Performance

  1. Async concurrency: "This code calls [API/DB] in a loop. Rewrite it to run [N] at a time using Promise.all with a concurrency limit. Keep the types correct."
  2. Lazy initialization: "This service initializes a heavy resource on every request. Refactor it to initialize once on first use and cache the result. Handle concurrent initialization."
  3. Stream processing: "Rewrite this function that loads a large array into memory to use Node.js streams instead. Maintain TypeScript types throughout."

API & validation

  1. Zod schema builder: "Generate a Zod schema for this TypeScript interface. Include appropriate validators for strings (min/max length), numbers (positive, integer), and optional fields."
  2. API response types: "I get this response shape from [external API]. Generate TypeScript types, a Zod validation schema, and a fetch wrapper that returns typed data or a typed error."
  3. OpenAPI types: "Generate TypeScript interfaces from this OpenAPI spec endpoint: [paste endpoint]. Include request body, path params, query params, and all response shapes."

Node.js specifics

  1. Worker thread: "Extract this CPU-intensive function into a Node.js worker thread. Type the messages passed between main thread and worker using a discriminated union."
  2. Graceful shutdown: "Add graceful shutdown handling to this Express app. It should stop accepting new requests, drain in-flight requests, close DB connections, and exit cleanly. Type it properly."
  3. Config validation: "Write a Zod schema that validates all required environment variables at startup. The app should fail immediately with a clear error if any are missing or the wrong type."
  4. Path module replacement: "Replace all path.join() calls in this file with proper ESM import.meta.url patterns for Node.js 22. Keep the same TypeScript types."

Common mistakes TypeScript devs make with Claude Code

Mistake 1: Asking for code before sharing your types

If you ask Claude to "add a user endpoint" without sharing your User type, Claude will invent one. Always open sessions by pointing Claude at your types directory: "The shared types for this project are in src/types/. Read them before writing any code."

Mistake 2: Accepting `any` without pushback

Claude will reach for any when it's uncertain. Push back explicitly: "Don't use any. If you're not sure about the type, use unknown and add a comment explaining what it should be." Add this to your CLAUDE.md and it becomes automatic.

Mistake 3: Not specifying your tsconfig settings

Whether strict, exactOptionalPropertyTypes, or noUncheckedIndexedAccess are enabled changes what valid TypeScript looks like. Claude doesn't know your tsconfig. Tell it: "Strict mode is on. Treat undefined array access as potentially undefined."

Mistake 4: Asking for tests without specifying the mock library

Jest and Vitest have compatible APIs but different import paths. Specify your framework and version, or Claude may generate tests that don't run.

Mistake 5: Not using CLAUDE.md for your conventions

If you don't write down your conventions, Claude will invent its own — and they'll be different from yours. The 20 minutes to write a good CLAUDE.md saves hundreds of micro-corrections over time.

Quick check: If you're re-explaining the same convention to Claude on every session, it belongs in CLAUDE.md. Treat it as onboarding documentation for a new developer who forgets everything between shifts.

Get the CLAUDE.md Starter Kit

10 production-ready CLAUDE.md templates — TypeScript, Python, React, Go, and more. Slash command libraries, anti-pattern lists, and a 4-week team adoption guide. Instant download.

Get the Starter Kit — $19 →

What to read next