TypeScript Code Quality

Check every pull request with this TypeScript code review checklist to maintain strong typing, solid security and reliable performance before production.

What We Cover in the TypeScript Analysis​

Here’s what this checklist helps you review before approving a pull request.

Code & Design Quality – consistent naming and formatting, small focused functions, and clean modular structure
Type Safety & Contracts – strict typing, precise generics, safe narrowing and runtime validation
Async & Concurrency – explicit Promise types, proper cancellation, bounded retries and controlled parallelism
Error Handling & Observability – typed errors with context, structured logs, metrics and health checks
Security & Performance – validated inputs, safe DOM/secret handling, efficient data flows and scalable execution
Data & Integration – well-typed APIs and schemas, safe migrations and correct handling of dates, numbers and configuration

TypeScript Code Review Checklist

Export this Checklist as Markdown

Use these points as a starting guide to automate code reviews with your agent.

Naming is meaningful and consistent (files kebab-case; types/interfaces PascalCase; functions/variables camelCase; enums PascalCase).

Modules expose a minimal, focused surface (no “barrel dumping” everything from index.ts).

Functions are small and single-purpose; parameters ≤ ~5; avoid boolean flags—prefer options objects.

Comments explain “why,” not “what”; no stale or commented-out code; TODOs link to an issue.

Avoid clever type gymnastics in app code—move complex types to types.ts with docs.

Prettier formatting and ESLint pass; no ts-ignore/eslint-disable unless justified with a comment.

strict mode enabled; no implicit any; noImplicitOverride, noUncheckedIndexedAccess, exactOptionalPropertyTypes turned on when possible.

Prefer unknown over any; model runtime-validated inputs as unknown → refine to safe types.

Discriminated unions used over ad-hoc flags; exhaustive switch guarded by never.

Narrowing uses type predicates or assertion functions (not as casts); avoid non-null ! except at validated boundaries.

Use readonly for immutable structures and tuples where applicable.

Use satisfies to validate value shapes against types without widening.

Generics have clear type params (<TData, TError = unknown> with defaults where useful).

Constraints reflect intent (<T extends Record<string, unknown>>); avoid over-generic <T = any>.

Conditional/mapped/template-literal types are readable and tested; avoid exponential complexity.

Output types are computed via helpers (e.g., Pick, Omit, Exclude, NonNullable, Awaited)—don’t reinvent them.

Variance considered for callback params (don’t over-constrain contravariant positions).

Branded/opaque types used for IDs/unsafe primitives to prevent mix-ups.

All external inputs (env, JSON, network, user input) are validated at runtime before use.

Optional fields respect exactOptionalPropertyTypes; avoid foo?.bar?.() chains that hide errors.

Index access checks use noUncheckedIndexedAccess or guards.

Nullable flows narrowed with explicit guards; no reliance on truthiness for 0/”/false.

Non-null assertions ! appear only after rigorous validation or in performance-critical hot paths with comments.

Public APIs expose minimal, stable contracts; breaking changes are versioned.

Types exported for consumers are intentionally designed (no leaking internal helpers).

DTOs/requests/responses are declared once and reused across layers (derive from schema when possible).

Error contracts are typed (error shapes or Result<E, T> style); no throw string.

Backwards compatibility considered (deprecated fields documented with removal plan).

Interop with JS code uses clear boundary modules; inputs validated and narrowed at the edge.

JSDoc typings in JS files are accurate if mixed repo; no duplication between JSDoc and .d.ts.

Avoid default exports for libraries; named exports ease tree-shaking and refactors.

ESM/CJS interop handled via exports field and correct moduleResolution (e.g., nodenext).

allowJs/checkJs usage is intentional for migration, not permanent drift.

async functions have explicit return types (Promise<...>); no floating promises—guard with void or await.

Cancellation supported via AbortSignal or equivalent; propagated through layers.

Backoff with jitter for retries; max attempts bounded; idempotency keys for mutating calls.

Avoid forEach(async ...); prefer for...of with await, or Promise.allSettled with concurrency limits.

Timers/intervals cleaned up; no orphaned handles in long-lived services.

Shared state is synchronized (atomic updates, message queues, or locks when needed).

Throw Error (or subclasses) with cause/context; never throw plain strings.

Errors are mapped at boundaries to typed domain errors; avoid leaking infra details upward.

Logs are structured (key-value), typed, and redact secrets; correlation/trace IDs propagate.

Metrics (latency, saturation, errors) and health checks exist for critical paths.

try/catch blocks are minimal; don’t swallow errors—surface or log with level and action item.

Inputs sanitized/validated; no direct DOM injection; escape HTML; use textContent over innerHTML.

Avoid eval, Function, dynamic require; no unsafe any in security-sensitive areas.

Secrets not in source; use typed config; rotate and scope credentials; least privilege enforced.

HTTP security headers present on server; CSRF/XSRF, CORS, and content security policies set correctly.

Dependency risk managed (pin versions, review transitive impacts); sandbox untrusted code.

Hot paths avoid excessive allocations; prefer object reuse where safe; avoid megabyte-scale JSON in memory.

Data transforms fused where possible (map+filter combined); avoid O(n²) surprises.

Awareness of V8/JIT deopts (stable object shapes, avoid polymorphic megamorphic call sites).

Use streaming/iterators for large payloads; backpressure respected.

Avoid synchronous blocking in event loops (crypto, zlib) without worker threads.

Unit tests cover logic and edge cases; integration tests for boundaries; E2E for critical flows.

Type-level tests (e.g., expectTypeOf, tsd) validate public types and generics.

Property-based or fuzz tests for parsers/serializers; snapshot tests used sparingly and focused.

Mocks/fakes are typed; avoid over-mocking—prefer real modules in integration tests.

Coverage meaningful (branches/conditions), not just lines; flakiness tracked and fixed.

typescript-eslint rules enforce safety: no-unsafe-*, no-floating-promises, restrict-template-expressions, consistent-type-imports.

Lint and typecheck run in CI with --noEmit; failing PRs block merge.

Dead code detection and dependency checks in CI; bundle size budgets enforced where applicable.

Commit hooks run fast (e.g., lint-staged); no long-running local gates.

tsconfig per package with clear target, module, moduleResolution, lib; composite/project references for large repos.

Declarations (declaration, declarationMap) emitted for libraries; sourceMap on for debuggability.

isolatedModules compatible with the transpiler (swc/esbuild/babel) if used.

Reproducible builds (locked deps, pinned toolchain); environment-specific condition exports used instead of ad-hoc flags.

CI caches TypeScript build info; incremental builds enabled locally.

Proper package.json fields: name, version, type, exports, types, files, sideEffects, engines, peerDependencies.

Dual ESM/CJS support via conditional exports; no deep import leaks.

typesVersions provided if necessary; public API surface minimal and documented.

Monorepo uses workspaces (pnpm/npm/yarn), project refs, and consistent tooling; no cross-package path hacks.

Changesets or equivalent for versioning/release notes; publish pipeline typed and automated.

React: components typed (FC avoided for children), props/interfaces minimal, hooks dependencies correct, useMemo/useCallback purposeful.

Next.js/Nuxt/SvelteKit: server/client boundaries typed; no secret leakage to client; route handlers typed and validated.

Node/Nest/Express/Fastify: schemas (zod/yup/valibot) validate at edges; handlers return typed results/errors.

Angular: strictTemplates enabled; inputs/outputs typed; DI tokens typed and scoped.

State management (Redux/Zustand/MobX/etc.) has typed selectors/actions; side effects isolated and testable.

HTTP clients are typed; request/response models derived from a single schema source (OpenAPI → types or zod → OpenAPI).

Retries use idempotent methods only; timeouts and AbortSignal in place; circuit breakers for flaky deps.

Pagination, rate limits, and partial failures handled; streaming where applicable.

Authentication flows strongly typed; tokens/claims validated; clock skew handled.

WebSocket/Server-Sent Events types for messages and lifecycle events.

DB access layer typed end-to-end (e.g., Prisma/Typed SQL); migrations reviewed; defaults and constraints reflected in types.

bigint/decimal handled safely (no silent number coercion); dates/timezones explicit (Temporal/Date strategy documented).

Serialization formats typed and versioned; backward compatibility planned.

Caching layers typed; invalidation keys modeled; TTLs explicit.

Clear boundaries between domain/app/infra; dependencies flow inward; no circular imports.

Public vs. internal types separated; unstable internals not exported.

Feature modules cohesive; avoid “god” utility modules; avoid overuse of re-exports that obscure ownership.

ADRs or lightweight design notes exist for non-obvious decisions; complexity budget respected.

Prefer composition over inheritance; Strategy/Command/Factory used where they reduce complexity.

Result/Either types over exceptions for expected failures; never for logic holes.

Builders fluent but typed (no unsafe chaining); brand types protect invariants.

Observables/iterators/async iterables typed across producers/consumers.

Config read once, validated (zod/valibot) and exported as typed singleton; no ad-hoc process.env access in app code.

Feature flags typed (enum/union) with explicit rollout strategy; defaults safe; guards centralized.

Client/server flag evaluation boundaries respected; no leaking server-only flags to client bundles.

Twelve-factor: stateless services, config via env (typed), logs to stdout in JSON, graceful shutdown with signal handling.

Health/readiness probes typed and reflect real dependencies.

OpenTelemetry types for traces/metrics/logs; context propagation typed across async boundaries.

Resource limits/timeouts/cancellations enforced in code and infra config.

CLIs typed (e.g., oclif/commander) with clear help, validation, and exit codes.

Shell completions and --json machine output where relevant.

Error messages actionable; verbose/debug modes; telemetry gated and documented.

Fast local dev loop (tsx/esbuild) without compromising type safety.

any usage forbidden except at narrow, documented boundaries; prefer unknown + refinement.

as casts minimized; if required, justified; no double-casting to bypass compiler.

Declaration merging/module augmentation documented and covered by tests.

ts-ignore/expect-error lines include rationale and a ticket to remove; time-boxed.

const assertions and satisfies used to keep literals precise without widening.

Turn this checklist into automated reviews

Let Kody automatically check quality, security and performance in every pull request—cloud or self-hosted, in under 2 minutes.