Same Ecosystem, Different Type Systems
JavaScript and TypeScript share: the npm ecosystem, the same frameworks (React, Next.js, Express), and largely the same coding patterns. But the type system creates a fundamental difference in AI rules. TypeScript: the AI should generate type annotations, use strict mode features, prefer interfaces for object shapes, and never fall back to any. JavaScript: the AI should not generate TypeScript syntax (: string, interface, type — these cause syntax errors in .js files), should use JSDoc for type hints if the project uses them, and should rely on runtime validation (Zod) where TypeScript provides compile-time safety.
Without language-specific rules: the AI defaults to TypeScript patterns (it has more TypeScript training data from modern codebases). In a JavaScript project: the AI generates const name: string = 'Alice' (syntax error in .js). In a TypeScript project without strict rules: the AI uses any liberally (defeats the purpose of TypeScript). The language rule is: the most fundamental rule in any rule file. It determines whether every line of AI-generated code is syntactically valid.
This article provides: the specific rules needed for TypeScript projects (strict mode, type conventions, generic guidance), the specific rules needed for JavaScript projects (no TS syntax, JSDoc, runtime validation), and migration rules (projects transitioning from JavaScript to TypeScript). The language rule should be: the first line of every CLAUDE.md.
TypeScript-Specific Rules
Essential TypeScript rules: "TypeScript strict mode (strict: true in tsconfig.json). Never use any — use unknown and narrow with type guards or Zod. Prefer interfaces for object shapes (extensible with declaration merging). Use type for unions, intersections, and utility types. Export types alongside the values that use them. Use generics for reusable functions and components (React.FC is discouraged — use function declaration with typed props)."
Type annotation conventions: "Annotate function return types for exported functions (enables better error messages and documentation). Infer types for local variables (const name = 'Alice' not const name: string = 'Alice' — TypeScript infers correctly). Use z.infer<typeof schema> for Zod schema types (single source of truth). Use satisfies for type checking without widening (const config = { ... } satisfies Config). Use as const for literal types (const ROLES = ['admin', 'editor'] as const)."
These rules tell the AI: how to use TypeScript idiomatically. Without them: the AI generates: any for complex generics (lazy fallback), redundant type annotations on every variable (noisy), class-based patterns with decorators (not idiomatic modern TypeScript), and separate type definition files (.d.ts for everything, even when co-located types are cleaner). The TypeScript rules: guide the AI toward modern, strict, idiomatic TypeScript that the team expects.
- strict: true. No any — unknown + type guards or Zod instead
- interfaces for objects (extensible). type for unions and intersections
- Annotate exported returns. Infer local variables. z.infer for Zod schemas
- satisfies for type checking without widening. as const for literal types
- No React.FC — function declaration with typed props parameter
TypeScript best practice: annotate return types on exported functions (better errors and documentation). Infer types on local variables (const name = 'Alice' not const name: string = 'Alice' — TypeScript infers correctly). The AI without this rule: annotates every variable redundantly. With the rule: clean, idiomatic TypeScript.
JavaScript-Specific Rules
Essential JavaScript rules: "JavaScript project — do not generate TypeScript syntax. No type annotations (: string, : number), no interfaces, no type keyword, no generics (<T>), no enum. Files are .js and .jsx, not .ts and .tsx. For type hints: use JSDoc comments (/** @param {string} name */ or /** @type {import('./types').User} */). Runtime validation: use Zod for input validation (provides type-like safety at runtime without TypeScript compilation)."
Why the JavaScript rule is critical: AI models have extensive TypeScript training data. Without the JavaScript rule: the AI adds TypeScript syntax to .js files. The code looks correct in the AI response but: fails on execution (: string is a syntax error in JavaScript). The developer must: manually remove every type annotation. The JavaScript rule prevents: the most common AI error in JavaScript projects (generating TypeScript syntax). One rule prevents: every syntax error from TypeScript leakage.
JSDoc type conventions: "Use JSDoc @param and @returns for function documentation and type hints. Use @typedef for complex types. Use @type for variable type hints. IDE support: VS Code reads JSDoc and provides IntelliSense even in .js files. JSDoc provides: documentation + type hints without TypeScript compilation. For projects that want type safety without TypeScript: JSDoc + checkJs: true in tsconfig.json enables TypeScript type checking on JavaScript files."
- Critical: 'No TypeScript syntax in .js files' — prevents the most common AI error in JS projects
- No: type annotations, interfaces, type keyword, generics, enum — all cause .js syntax errors
- JSDoc: @param, @returns, @typedef, @type — type hints without TypeScript compilation
- Runtime validation: Zod for input validation — runtime type safety without TS compiler
- checkJs: true in tsconfig: enables TS type checking on .js files with JSDoc types
'JavaScript project — no TypeScript syntax.' One sentence. Prevents: const name: string (syntax error), interface User (syntax error), type Props (syntax error), <T> generics (syntax error). Without this rule: the AI generates TypeScript in .js files because it has more TypeScript training data.
Rules for JavaScript-to-TypeScript Migration
Migration-phase rules: "This project is migrating from JavaScript to TypeScript. New files: always .ts/.tsx with full type annotations. Existing .js files: do not convert unless the task specifically requires it. When converting a .js file to .ts: add types gradually (start with function parameters and return types, then object shapes). Use // @ts-expect-error for known issues that will be fixed later (not @ts-ignore — @ts-expect-error fails when the error is fixed, reminding you to remove it)."
The migration rule prevents: the AI from converting files that should stay JavaScript (the migration is incremental, not big-bang), adding overly complex types to newly-converted files (start simple, refine later), and using @ts-ignore to suppress errors (hides real issues instead of tracking them). The migration is: file-by-file. The rules ensure: the AI participates in the migration at the right pace (new files in TypeScript, existing files converted only when explicitly requested).
Post-migration rules: once the project is fully TypeScript, remove the migration rules and use the standard TypeScript rules. The migration rules are: temporary. They serve: the transition period where .js and .ts files coexist. In the full TypeScript state: every rule about JSDoc, .js files, and gradual conversion is obsolete. Update the rule file to: reflect the current state of the project, not its history.
- New files: always .ts/.tsx with types. Existing .js: do not convert unless explicitly asked
- When converting: add types gradually. Parameters and returns first, then object shapes
- @ts-expect-error (not @ts-ignore): fails when error is fixed, reminds you to clean up
- Migration rules are temporary: remove when project is fully TypeScript
- Rule file reflects current state: update after migration completes
During JS-to-TS migration: @ts-expect-error suppresses a known issue but FAILS when the error is fixed (reminding you to remove the suppression). @ts-ignore: silently suppresses forever, even after the error is gone. The migration rule: use @ts-expect-error to track issues, not @ts-ignore to hide them permanently.
Rules for Mixed JavaScript/TypeScript Codebases
Some projects intentionally maintain both: .ts files for application code (type-safe), .js files for configuration (webpack.config.js, jest.config.js, scripts). The rule: "TypeScript for application code (.ts/.tsx in src/). JavaScript for configuration files (*.config.js, scripts/). Do not add TypeScript syntax to .js config files. Do not convert config files to TypeScript unless the config tool supports it natively."
The mixed-codebase challenge: the AI must know which language to use for each file. Without the rule: the AI may generate a webpack.config.ts (some tools do not support .ts config natively) or add type annotations to a .js script (syntax error). The rule provides: a clear boundary. Application code = TypeScript. Config files = JavaScript. Scripts = JavaScript (unless they import from the TypeScript application, in which case .ts is appropriate).
For monorepos with language diversity: packages/api/ (TypeScript), packages/ml/ (Python), scripts/ (JavaScript). The hierarchical CLAUDE.md handles this: root CLAUDE.md specifies the default (TypeScript). scripts/CLAUDE.md overrides for JavaScript. packages/ml/CLAUDE.md specifies Python. Each context gets: the correct language rules. The AI never generates: TypeScript in a Python file or Python in a JavaScript file.
Language Rule Summary
Summary of AI rules for JavaScript vs TypeScript.
- TypeScript: strict: true, no any, interfaces for objects, type for unions, annotate exports, infer locals
- JavaScript: no TypeScript syntax in .js, JSDoc for type hints, Zod for runtime validation
- Critical JS rule: 'No TypeScript syntax' prevents the #1 AI error in JavaScript projects
- Migration: new files in .ts, existing .js converted only on request, @ts-expect-error over @ts-ignore
- Mixed codebases: app code in TypeScript, config files in JavaScript, clear boundary rule
- Hierarchy: root CLAUDE.md (default language) + subdirectory overrides (language per context)
- Language rule = first line of CLAUDE.md: determines whether every generated line is valid syntax
- AI defaults to TypeScript (more training data): JavaScript projects MUST have an explicit language rule