TypeScript Standards
Default load: this file only. Pull refs/tooling.md, refs/testing.md, or refs/security.md only when the task explicitly needs that depth.
Priority: P0 — Type Correctness
Type Annotations
- Explicit params and return types on all public declarations. Infer locals.
- Avoid
any. Prefer unknown, generics, or a narrowly-scoped escape hatch with a comment when interop forces it.
- Never use the
Function type. Use a typed signature: () => void.
Interfaces vs Types
interface for object shapes that describe APIs — supports declaration merging.
type for unions, intersections, mapped types, and conditional types.
Strict Mode
strict: true in tsconfig. On existing repos, migrate incrementally: strictNullChecks → noImplicitAny → strictFunctionTypes. Never flip strict: true in one step.
- Avoid non-null assertion (
!). Use narrowing (typeof, instanceof, if-checks) instead.
?. and ?? for null safety — use narrowing, not !.
Enums
- Literal unions or
as const objects. No runtime enum.
Generics
- Use generics for reusable, type-safe code. Constrain with
extends where appropriate.
Type Guards
- Use
typeof, instanceof, and predicate functions (x is T) to narrow types.
Utility Types
- Prefer built-ins:
Partial, Required, Pick, Omit, Record, Readonly, NonNullable.
- Prefer
satisfies for object literals that must conform to a contract without widening the inferred type.
Immutability
readonly on arrays and object properties. Use as const and satisfies for const assertions.
Discriminated Unions
- Use a stable discriminant such as
kind or type to narrow safely. Switch on the discriminant.
type Result<T> = { kind: 'ok'; data: T } | { kind: 'err'; error: Error };
Branded Types
- Use brands only when structurally identical values such as IDs or units are easy to mix up across boundaries.
type UserId = string & { readonly __brand: 'UserId' };
function createUserId(id: string): UserId { return id as UserId; }
Exhaustiveness
- Use
never in switch default to catch unhandled union members at compile time.
Priority: P0 — Boundary Safety
External Data
- Treat data from I/O boundaries as untrusted until it is parsed, validated, and narrowed.
- Prefer schema-based validation at API and persistence boundaries when the project already uses a validator. Load
refs/security.md for concrete API/auth guidance.
Dangerous Sinks
- Never interpolate untrusted input into SQL, shell commands, HTML, filesystem paths, or externally-sourced URLs.
- Use parameterized queries, safe child-process APIs, output sanitization, and origin allowlists where the sink requires them.
Secrets
- Never hardcode secrets, tokens, or credentials in source.
- Never log secrets or raw auth tokens.
Priority: P1 — Code Conventions
Naming
PascalCase: classes, types, interfaces.
camelCase: variables, functions, methods.
UPPER_SNAKE_CASE: static constants only.
Functions
- Arrow functions for callbacks and inline logic. Function declarations for top-level exports.
- Always type return values on public API functions.
Modules
- Prefer named exports unless a framework or file convention requires a default export.
import type for interfaces and types — zero runtime overhead.
- Keep import grouping consistent with the repo. Load
refs/tooling.md when changing lint enforcement.
Async
- Prefer
async/await. Use Promise.all() only for independent work that can safely run in parallel.
try/catch with catch (e: unknown) — narrow before use. Avoid .then().catch() chains.
Classes
- Use explicit visibility when it protects internal state or materially improves readability. Avoid redundant
public churn unless the repo standard requires it.
- Favor composition over inheritance. Constructor injection with interfaces — not singletons.
Optional Chaining
?. and ?? over manual null checks.
Priority: P1 — Verification
After editing any .ts/.tsx file:
- Use TypeScript diagnostics from the editor, LSP, or MCP tooling when available.
- Run the repo's typecheck command (
tsc --noEmit, pnpm typecheck, or equivalent).
- Run the repo's lint and test commands for the changed surface. Use auto-fix only when the repo already expects it.
Fallback when no LSP tooling is configured: run the repo's typecheck command directly.
Inspect inferred types before adding annotations that may fight the compiler. Check references before large renames or signature changes.
Anti-Patterns
- Broad
any usage when unknown, generics, or a local escape hatch would work
Function type — use a typed signature () => void
- Runtime
enum — use literal unions or as const
- Non-null assertion
! — use narrowing
- Default exports where the framework or file convention does not require them
require() — use ES6 import
- Empty interfaces — use
type or a non-empty interface
- Unsafe mock casts — use
jest.Mocked<T> or as unknown as T
@ts-ignore — use @ts-expect-error (self-documents intent; fails if the error disappears)
- Global
eslint-disable — suppress per-line; fix root cause
- Atomic
strict: true flip on an existing repo — migrate incrementally starting with strictNullChecks
eval, Function constructor, or string literals as timer callbacks
- Shell string interpolation with untrusted input (
execSync(\cmd ${userInput}`)`)
- Unvalidated externally-sourced URLs passed to network or redirect APIs
- Plaintext secrets in code, tests, fixtures, or Git
References
Load only what the current task requires:
- tooling — configuring tsconfig, ESLint, Jest, Vitest, build pipeline, or CI
- testing — writing, debugging, or reviewing tests
- security — input validation, authentication, JWT, secrets, or API security
Do not load refs for ordinary type-shape, refactor, or local implementation tasks.
1---2name: typescript3description: TypeScript 5.x language standards for type safety, narrowing, generics, modules, and async code. Use for TypeScript implementation or review work; load refs only for tooling, testing, or security-specific tasks.4---56# TypeScript Standards78Default load: this file only. Pull `refs/tooling.md`, `refs/testing.md`, or `refs/security.md` only when the task explicitly needs that depth.910## Priority: P0 — Type Correctness1112### Type Annotations13- Explicit params and return types on all public declarations. Infer locals.14- Avoid `any`. Prefer `unknown`, generics, or a narrowly-scoped escape hatch with a comment when interop forces it.15- Never use the `Function` type. Use a typed signature: `() => void`.1617### Interfaces vs Types18- `interface` for object shapes that describe APIs — supports declaration merging.19- `type` for unions, intersections, mapped types, and conditional types.2021### Strict Mode22- `strict: true` in tsconfig. On existing repos, migrate incrementally: `strictNullChecks` → `noImplicitAny` → `strictFunctionTypes`. Never flip `strict: true` in one step.23- Avoid non-null assertion (`!`). Use narrowing (`typeof`, `instanceof`, if-checks) instead.24- `?.` and `??` for null safety — use narrowing, not `!`.2526### Enums27- Literal unions or `as const` objects. No runtime `enum`.2829### Generics30- Use generics for reusable, type-safe code. Constrain with `extends` where appropriate.3132### Type Guards33- Use `typeof`, `instanceof`, and predicate functions (`x is T`) to narrow types.3435### Utility Types36- Prefer built-ins: `Partial`, `Required`, `Pick`, `Omit`, `Record`, `Readonly`, `NonNullable`.37- Prefer `satisfies` for object literals that must conform to a contract without widening the inferred type.3839### Immutability40- `readonly` on arrays and object properties. Use `as const` and `satisfies` for const assertions.4142### Discriminated Unions43- Use a stable discriminant such as `kind` or `type` to narrow safely. Switch on the discriminant.4445```typescript46type Result<T> = { kind: 'ok'; data: T } | { kind: 'err'; error: Error };47```4849### Branded Types50- Use brands only when structurally identical values such as IDs or units are easy to mix up across boundaries.5152```typescript53type UserId = string & { readonly __brand: 'UserId' };54function createUserId(id: string): UserId { return id as UserId; }55```5657### Exhaustiveness58- Use `never` in switch `default` to catch unhandled union members at compile time.5960---6162## Priority: P0 — Boundary Safety6364### External Data65- Treat data from I/O boundaries as untrusted until it is parsed, validated, and narrowed.66- Prefer schema-based validation at API and persistence boundaries when the project already uses a validator. Load `refs/security.md` for concrete API/auth guidance.6768### Dangerous Sinks69- Never interpolate untrusted input into SQL, shell commands, HTML, filesystem paths, or externally-sourced URLs.70- Use parameterized queries, safe child-process APIs, output sanitization, and origin allowlists where the sink requires them.7172### Secrets73- Never hardcode secrets, tokens, or credentials in source.74- Never log secrets or raw auth tokens.7576---7778## Priority: P1 — Code Conventions7980### Naming81- `PascalCase`: classes, types, interfaces.82- `camelCase`: variables, functions, methods.83- `UPPER_SNAKE_CASE`: static constants only.8485### Functions86- Arrow functions for callbacks and inline logic. Function declarations for top-level exports.87- Always type return values on public API functions.8889### Modules90- Prefer named exports unless a framework or file convention requires a default export.91- `import type` for interfaces and types — zero runtime overhead.92- Keep import grouping consistent with the repo. Load `refs/tooling.md` when changing lint enforcement.9394### Async95- Prefer `async/await`. Use `Promise.all()` only for independent work that can safely run in parallel.96- `try/catch` with `catch (e: unknown)` — narrow before use. Avoid `.then().catch()` chains.9798### Classes99- Use explicit visibility when it protects internal state or materially improves readability. Avoid redundant `public` churn unless the repo standard requires it.100- Favor composition over inheritance. Constructor injection with interfaces — not singletons.101102### Optional Chaining103- `?.` and `??` over manual null checks.104105---106107## Priority: P1 — Verification108109After editing any `.ts`/`.tsx` file:1101111. Use TypeScript diagnostics from the editor, LSP, or MCP tooling when available.1122. Run the repo's typecheck command (`tsc --noEmit`, `pnpm typecheck`, or equivalent).1133. Run the repo's lint and test commands for the changed surface. Use auto-fix only when the repo already expects it.114115> Fallback when no LSP tooling is configured: run the repo's typecheck command directly.116117Inspect inferred types before adding annotations that may fight the compiler. Check references before large renames or signature changes.118119---120121## Anti-Patterns122123- Broad `any` usage when `unknown`, generics, or a local escape hatch would work124- `Function` type — use a typed signature `() => void`125- Runtime `enum` — use literal unions or `as const`126- Non-null assertion `!` — use narrowing127- Default exports where the framework or file convention does not require them128- `require()` — use ES6 `import`129- Empty interfaces — use `type` or a non-empty interface130- Unsafe mock casts — use `jest.Mocked<T>` or `as unknown as T`131- `@ts-ignore` — use `@ts-expect-error` (self-documents intent; fails if the error disappears)132- Global `eslint-disable` — suppress per-line; fix root cause133- Atomic `strict: true` flip on an existing repo — migrate incrementally starting with `strictNullChecks`134- `eval`, `Function` constructor, or string literals as timer callbacks135- Shell string interpolation with untrusted input (`execSync(\`cmd ${userInput}\`)`)136- Unvalidated externally-sourced URLs passed to network or redirect APIs137- Plaintext secrets in code, tests, fixtures, or Git138139---140141## References142143Load only what the current task requires:144145- [tooling](refs/tooling.md) — configuring tsconfig, ESLint, Jest, Vitest, build pipeline, or CI146- [testing](refs/testing.md) — writing, debugging, or reviewing tests147- [security](refs/security.md) — input validation, authentication, JWT, secrets, or API security148149Do not load refs for ordinary type-shape, refactor, or local implementation tasks.