Contents
- Why TypeScript + Claude Code is a force multiplier
- CLAUDE.md template for TypeScript/Node.js projects
- Type error debugging patterns
- Framework-specific workflows (NestJS, Next.js, Express)
- Prisma & database workflows
- Testing with Vitest and Jest
- 20 high-value TypeScript prompts
- 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.
| Scenario | JavaScript | TypeScript |
|---|---|---|
| Generate a function | Claude guesses arg types, often wrong | Claude reads interfaces, types correctly on first try |
| Refactor a module | Easy to break implicit contracts | Claude sees explicit contracts via types |
| Add a new endpoint | Claude invents its own structure | Claude matches existing DTOs and response schemas |
| Debug an error | Runtime errors only | Claude 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
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:
Pattern 2: Generic constraint errors
Generic constraint errors are notoriously cryptic. Claude handles them well if you explain what you're trying to accomplish:
Pattern 3: `strictNullChecks` errors
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
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:
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.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
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.
20 high-value TypeScript prompts
Type system
- 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."
- Mapped type builder: "Create a mapped type that takes [InputType] and produces a version where [transformation]. Show how to use it with an example."
- 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."
- 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."
- Template literal types: "Generate a template literal type that [describes the pattern, e.g., 'validates event names follow `on${EventName}` format']."
Refactoring
- 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."
- 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."
- 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."
- 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."
- 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
- 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."
- 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."
- Stream processing: "Rewrite this function that loads a large array into memory to use Node.js streams instead. Maintain TypeScript types throughout."
API & validation
- 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."
- 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."
- 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
- 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."
- 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."
- 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."
- 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.
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
- Claude Code for Python developers — same depth, Python/Django/FastAPI angle
- CLAUDE.md best practices — deeper dive on writing effective project context
- Claude Code hooks — automate linting and tests on every save
- Claude Code MCP guide — connect TypeScript tools to Claude's context