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
asto 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-ignoreor@ts-expect-errorwithout justification
Common Mistakes
Type Safety Issues
- Using
anytype (defeats TypeScript) → Useunknownwith type guards - Type assertions without validation (
data as Type) → Runtime validation with Zod - Missing function parameter/return type annotations → Function signatures
@ts-ignoreor@ts-expect-errorwithout 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
nullwhenundefinedor throwing would be clearer
Configuration Issues
strict: falsein tsconfig.json → Must usestrict: truestrictNullChecks: false(dangerous)- Missing
noUncheckedIndexedAccess(array access unsafe) noImplicitAny: false(allows implicit any types)skipLibCheck: truewithout understanding implications
Type Definition Issues
- Using
interfacewhentypeis more appropriate (unions, intersections) → Interfaces vs Types - Regular enums instead of string literal unions → Enums vs String Literals
- Not using
readonlyon 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
neverfor exhaustiveness checking - Overusing
Partial<T>when specific optional fields needed → UsePartialBy<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
inferfor type extraction when needed → Conditional Types
Watch For - Best Practices
Immutability
- Missing
readonlyon properties → Readonly properties - Mutating arrays/objects directly instead of creating new ones
- Not using
constfor values that won't change - Not using
as constfor 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
anyinstead ofunknown - 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 (
@/forsrc/) → 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
- Strict mode enabled (
strict: true) → TypeScript Configuration - Explicit function parameter and return types → Function signatures
- Runtime validation for external data with Zod → Runtime validation
- Discriminated unions for state management → Discriminated Unions
- Type guards with
ispredicates → Type guards - Utility types for transformations (Partial, Pick, Omit) → Utility types
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) strictNullChecksenabled- Union with
null/undefinedwhen values can be absent
Immutability
readonlyon properties that shouldn't change → Immutabilityreadonlyarrays for function parametersconstfor values that won't be reassignedas constfor 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
| Category | What to Check | Severity | Reference |
|---|---|---|---|
| Security | No type assertions without validation | Block | Runtime Validation |
| Security | No eval() or Function() constructor | Block | Security best practices |
| Security | No API keys in source code | Block | Configuration |
| Security | Validate all external data at runtime | Block | Zod Validation |
| Type Safety | No any types | Fix | Avoid any |
| Type Safety | Strict mode enabled | Block | tsconfig.json |
| Type Safety | Function params/returns typed | Fix | Functions |
| Type Safety | No @ts-ignore without justification | Fix | Code quality |
| Null Checks | No non-null assertions (!) | Fix | Null Safety |
| Null Checks | Optional chaining used | Review | Null Safety |
| Immutability | readonly on immutable properties | Review | Readonly |
| Immutability | No direct mutation | Fix | Immutability |
| Enums | String literal unions (not enums) | Review | Enums vs Literals |
| Validation | Zod schemas for API boundaries | Fix | Zod |
| Error Handling | Custom error classes | Review | Errors |
| Imports | Named exports (not default) | Review | Best practices |
| Imports | import type for types | Review | Bundle optimization |
| Testing | Mutation testing passing | Review | Stryker |
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: truewith 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
satisfiesoperator 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: trueenabled in tsconfig.json - No
anytypes (or justified with comments) - All external data validated at runtime (Zod/validator)
- No type assertions without validation
- No
@ts-ignoreor@ts-expect-errorwithout 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
anyin 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
Related Documentation
TypeScript Guides:
- TypeScript General Guidelines - Fundamentals, type safety, best practices
- TypeScript Types - Advanced type patterns
- TypeScript Testing - Jest, Testing Library, Stryker
Framework-Specific:
- React Code Review Checklist - React + TypeScript
- Angular Code Review Checklist - Angular + TypeScript
- React General - TypeScript with React
- Angular General - TypeScript with Angular
Cross-Cutting:
- Security Best Practices
- Input Validation
- API Integration - Frontend - OpenAPI + TypeScript
Process: