Skip to main content

TypeScript Code Review Checklist

Rapid-scan checklist for TypeScript code reviews. Use this to quickly identify issues, then dive into linked documentation for details.


Security Red Flags - Block PR Immediately

  • Type assertions bypassing validation: Using as to cast API responses without runtime validation → Use Zod for runtime validation
  • dangerouslySetInnerHTML without sanitization: HTML injection vulnerability (React) → Input Validation
  • eval() or Function() constructor: Code execution vulnerabilities
  • Unsafe JSON parsing: JSON.parse(userInput) without validation/error handling
  • API keys or secrets in source code: Hardcoded credentials in constants, environment files committed to repo
  • Credentials in localStorage: Tokens, passwords in localStorage instead of secure httpOnly cookies
  • Missing input validation: User input not validated at runtime (TypeScript types don't exist at runtime)
  • XSS vulnerabilities: User input rendered without escaping/sanitization
  • SQL injection (backend): String concatenation in SQL queries instead of parameterized queries
  • Disabled TypeScript checks: Using @ts-ignore or @ts-expect-error without justification

Common Mistakes

Type Safety Issues

  • Using any type (defeats TypeScript) → Use unknown with type guards
  • Type assertions without validation (data as Type) → Runtime validation with Zod
  • Missing function parameter/return type annotations → Function signatures
  • @ts-ignore or @ts-expect-error without comment explaining why
  • Using ! non-null assertion instead of proper null handling → Null safety
// BAD: Type assertion without validation
const payment = apiResponse as Payment;

// GOOD: Runtime validation
const payment = PaymentSchema.parse(apiResponse);

Null/Undefined Handling

  • Missing null checks on potentially nullable values
  • Not using optional chaining (?.) for nested property access
  • Using non-null assertion (!) instead of explicit checks
  • Not using nullish coalescing (??) for default values
  • Returning null when undefined or throwing would be clearer

Configuration Issues

  • strict: false in tsconfig.json → Must use strict: true
  • strictNullChecks: false (dangerous)
  • Missing noUncheckedIndexedAccess (array access unsafe)
  • noImplicitAny: false (allows implicit any types)
  • skipLibCheck: true without understanding implications

Type Definition Issues

  • Using interface when type is more appropriate (unions, intersections) → Interfaces vs Types
  • Regular enums instead of string literal unions → Enums vs String Literals
  • Not using readonly on properties that shouldn't change
  • Mutable arrays when immutable would work (readonly T[])
  • Missing generic constraints (<T> instead of <T extends SomeType>)

Watch For - Type System & Correctness

Type Safety Gaps

  • Trusting external data without validation (APIs, user input, localStorage)
  • Missing discriminated unions for state management → Discriminated Unions
  • Not leveraging never for exhaustiveness checking
  • Overusing Partial<T> when specific optional fields needed → Use PartialBy<T, Keys>
  • Complex types without explanatory comments

Runtime vs Compile Time

  • Assuming types exist at runtime (they don't!)
  • Not validating API responses at runtime → Zod validation
  • Type guards without actual runtime checks
  • Missing Zod schemas for external data boundaries
  • Type assertions on unverified data

Function Issues

  • Functions longer than 40-50 lines (hard to follow)
  • Missing early returns for validation
  • Complex logic in single expression (hurts readability)
  • Not using function overloads when appropriate → Function Overloads
  • Async functions not properly typed with Promise<T>

Generics Misuse

  • Overusing generics when concrete types would work
  • Missing generic constraints (too broad)
  • Complex generic types without documentation
  • Not using infer for type extraction when needed → Conditional Types

Watch For - Best Practices

Immutability

  • Missing readonly on properties → Readonly properties
  • Mutating arrays/objects directly instead of creating new ones
  • Not using const for values that won't change
  • Not using as const for literal values that should be narrow types
// BAD: Mutable array
function getIds(payments: Payment[]): string[] {
// Function can mutate input
}

// GOOD: Readonly array
function getIds(payments: readonly Payment[]): readonly string[] {
return payments.map(p => p.id);
}

Error Handling

  • Catching errors as any instead of unknown
  • Not using custom error classes → Type-safe errors
  • Throwing strings instead of Error objects
  • Missing error handling in async functions
  • Not using Result types for expected failures → Result type pattern

Import/Export Issues

  • Default exports (harder to refactor) → Use named exports
  • Circular dependencies between modules
  • Importing types as values (bundle size increase) → Use import type { ... }
  • Not using path aliases (@/ for src/) → Configure in tsconfig.json

Code Organization

  • Type definitions scattered instead of in dedicated files
  • Large type files (> 300 lines) without logical grouping
  • Missing barrel exports (index.ts) for clean imports
  • Types defined inline instead of reused
  • Not using types/ directory for shared types

Best Practices - Code Quality

Type System

Advanced Patterns

  • Branded types for domain safety (IDs, tokens) → Branded Types
  • Mapped types for systematic transformations → Mapped Types
  • Template literal types for type-safe strings → Template Literals
  • Conditional types for type-level logic → Conditional Types
  • Generic constraints for specific use cases

Null Safety

  • Optional chaining (?.) for nested access → Null safety
  • Nullish coalescing (??) for defaults
  • Explicit null handling (not ! assertion)
  • strictNullChecks enabled
  • Union with null/undefined when values can be absent

Immutability

  • readonly on properties that shouldn't change → Immutability
  • readonly arrays for function parameters
  • const for values that won't be reassigned
  • as const for literal types
  • Immutable updates (spread operators, not mutation)

Testing

  • Tests type-safe (not using any) → TypeScript Testing
  • Runtime validation tested
  • Edge cases covered (null, undefined, boundary values)
  • Mutation testing configured (Stryker) → Mutation Testing
  • Type guards tested with both branches

Quick Scan Table

CategoryWhat to CheckSeverityReference
SecurityNo type assertions without validationBlockRuntime Validation
SecurityNo eval() or Function() constructorBlockSecurity best practices
SecurityNo API keys in source codeBlockConfiguration
SecurityValidate all external data at runtimeBlockZod Validation
Type SafetyNo any typesFixAvoid any
Type SafetyStrict mode enabledBlocktsconfig.json
Type SafetyFunction params/returns typedFixFunctions
Type SafetyNo @ts-ignore without justificationFixCode quality
Null ChecksNo non-null assertions (!)FixNull Safety
Null ChecksOptional chaining usedReviewNull Safety
Immutabilityreadonly on immutable propertiesReviewReadonly
ImmutabilityNo direct mutationFixImmutability
EnumsString literal unions (not enums)ReviewEnums vs Literals
ValidationZod schemas for API boundariesFixZod
Error HandlingCustom error classesReviewErrors
ImportsNamed exports (not default)ReviewBest practices
Importsimport type for typesReviewBundle optimization
TestingMutation testing passingReviewStryker

Legend:

  • Block PR - Security vulnerability or type safety violation
  • Fix Before Merge - Type safety issue, correctness problem, or likely bug
  • Review & Discuss - Code quality improvement

Additional Considerations

Advanced Type Issues

  • Not using discriminated unions for complex state → State Machines
  • Missing exhaustiveness checking with never
  • Branded types not used for IDs/tokens → Branded Types
  • Recursive types without proper termination conditions
  • Variance issues with function types

Performance Considerations

  • Large union types (> 50 members) causing slow compilation
  • Deeply nested conditional types (compilation performance)
  • Type inference failures forcing explicit annotations
  • Circular type references causing errors
  • Missing skipLibCheck: true with slow third-party types

Build Configuration

  • Missing source maps for debugging
  • Development code not tree-shaken in production
  • Bundle size over 500KB (initial load)
  • Missing code splitting configuration
  • Type checking not in CI/CD pipeline

Modern TypeScript Features

  • Not using template literal types for type-safe strings → Template Literals
  • Not using satisfies operator for type validation (TS 4.9+)
  • Not using const type parameters (TS 5.0+)
  • Missing utility types (Awaited, ReturnType, etc.) → Utility Types

React-Specific (if applicable)

  • Component props not typed → React General
  • Event handlers not typed (React.MouseEvent, React.ChangeEvent)
  • useState/useRef missing generic types
  • Custom hooks not typed properly
  • Missing React.FC or explicit return types on components

Node.js Backend (if applicable)

  • Express request/response not typed
  • Missing types for database models
  • Not using DTO validation (class-validator, Zod)
  • Environment variables not typed
  • Missing error middleware types

Block Checklist - Must Fix Before Merge

  • strict: true enabled in tsconfig.json
  • No any types (or justified with comments)
  • All external data validated at runtime (Zod/validator)
  • No type assertions without validation
  • No @ts-ignore or @ts-expect-error without explanation
  • No non-null assertions (!) without justification
  • Function parameters and return types explicitly typed
  • No hardcoded secrets or API keys
  • No dangerouslySetInnerHTML without sanitization (React)
  • No eval() or Function() constructor usage
  • strictNullChecks enabled
  • Proper error handling in all async functions

TypeScript-Specific Testing Checklist

Before approving TypeScript PRs:

  • Type-safe tests (no any in test code) → TypeScript Testing
  • Runtime validation tested (Zod schemas)
  • Type guards tested with both true/false branches
  • Edge cases covered (null, undefined, empty arrays)
  • Async code properly tested
  • Mocks properly typed
  • Mutation testing passing threshold (80%+) → Stryker
  • No TypeScript compilation errors in test files
  • Test factories for complex types
  • No ignored TypeScript errors in tests

TypeScript Guides:

Framework-Specific:

Cross-Cutting:

Process: