home / skills / physics91 / claude-vibe / typescript-reviewer

typescript-reviewer skill

/skills/typescript-reviewer

This skill reviews TypeScript code for type safety, tsconfig optimization, and pattern improvements to boost reliability and maintainability.

npx playbooks add skill physics91/claude-vibe --skill typescript-reviewer

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
10.7 KB
---
name: typescript-reviewer
description: |
  WHEN: TypeScript code review, type safety audit, tsconfig analysis, TS migration review
  WHAT: Type safety checks + any usage audit + generic patterns + strict mode + compiler options analysis
  WHEN NOT: React specific → nextjs-reviewer, Node.js backend → nodejs-reviewer, General code → code-reviewer
---

# TypeScript Reviewer Skill

## Purpose
Reviews TypeScript code for type safety, best practices, and idiomatic patterns. Identifies `any` abuse, missing type annotations, and unsafe type operations.

## When to Use
- TypeScript code review requests
- "Type safety", "any audit", "TS patterns" mentions
- TypeScript migration review
- tsconfig.json optimization
- Projects with `tsconfig.json` or `.ts`/`.tsx` files

## Project Detection
- `tsconfig.json` present
- `.ts` or `.tsx` files
- `typescript` in package.json dependencies
- `@types/*` packages in devDependencies

## Workflow

### Step 1: Analyze Project
```
**TypeScript**: 5.x
**Target**: ES2022
**Module**: ESNext
**Strict Mode**: ✓ enabled
**Key Flags**: strictNullChecks, noImplicitAny
```

### Step 2: Select Review Areas
**AskUserQuestion:**
```
"Which TypeScript areas to review?"
Options:
- Full type safety audit (recommended)
- any/unknown usage
- Generic patterns
- Type assertions
- Compiler options
multiSelect: true
```

## Detection Rules

### Critical: Type Safety Bypasses
| Pattern | Issue | Severity |
|---------|-------|----------|
| Explicit `any` | Bypasses type system | CRITICAL |
| `as any` casting | Type escape hatch | CRITICAL |
| `// @ts-ignore` | Silences all errors | CRITICAL |
| `// @ts-nocheck` | Disables file checking | CRITICAL |
| `any[]` array | Untyped collections | HIGH |
| Function returns `any` | Propagates unsafety | HIGH |
| `Promise<any>` | Async unsafety | HIGH |
| `Function` type | Too broad | HIGH |
| `Object` / `{}` type | Almost anything | HIGH |
| `Record<string, any>` | Indexed unsafety | HIGH |

```typescript
// BAD: Explicit any
function process(data: any): any {
  return data.value;  // No type safety
}

// GOOD: Proper typing
function process<T extends { value: V }, V>(data: T): V {
  return data.value;
}

// BAD: any array
const items: any[] = [];

// GOOD: Typed array
const items: Item[] = [];
// or if truly dynamic:
const items: unknown[] = [];

// BAD: @ts-ignore without justification
// @ts-ignore
const value = unsafeOperation();

// ACCEPTABLE: @ts-expect-error with justification
// @ts-expect-error - Legacy API returns wrong type, tracked in JIRA-123
const legacyValue = legacyApi.call();

// BAD: Broad types
const handler: Function = () => {};
const data: Object = {};
const config: Record<string, any> = {};

// GOOD: Specific types
const handler: () => void = () => {};
const data: Record<string, unknown> = {};
const config: AppConfig = {};
```

### High: Type Assertions (`as`) and External Data
| Pattern | Issue | Severity |
|---------|-------|----------|
| `as Type` on external data | No runtime validation | CRITICAL |
| `as unknown as Type` | Double assertion | HIGH |
| `as Type` on internal data | Unsafe assumption | MEDIUM |
| `<Type>value` (JSX conflict) | Legacy syntax | LOW |
| `!` non-null assertion | Runtime error risk | HIGH |
| `!` after guard/assertion | Acceptable | OK |

```typescript
// BAD: Blind assertion
const user = data as User;

// GOOD: Type guard first
function isUser(data: unknown): data is User {
  return typeof data === 'object' && data !== null && 'id' in data;
}
if (isUser(data)) {
  const user = data;  // Narrowed to User
}

// BAD: Non-null assertion
const name = user.profile!.name!;

// GOOD: Optional chaining with default
const name = user.profile?.name ?? 'Anonymous';

// BAD: Double assertion
const value = data as unknown as SpecificType;

// GOOD: Proper validation
function validate(data: unknown): SpecificType {
  if (!isSpecificType(data)) throw new Error('Invalid data');
  return data;
}

// CRITICAL: External data must be validated
// BAD: API response without validation
const user = await fetch('/api/user').then(r => r.json()) as User;

// GOOD: Validate external data with schema
import { z } from 'zod';
const UserSchema = z.object({ id: z.string(), name: z.string() });
const user = UserSchema.parse(await fetch('/api/user').then(r => r.json()));

// ACCEPTABLE: ! after explicit check
function process(items: Item[]) {
  const first = items.find(i => i.active);
  if (!first) throw new Error('No active item');
  return first.value;  // Safe - we just checked
}
```

### Medium: Missing Return Types
| Pattern | Issue | Severity |
|---------|-------|----------|
| No function return type | Inferred may be wrong | MEDIUM |
| Public API without types | Poor documentation | HIGH |
| Async without Promise<T> | Return type unclear | MEDIUM |

```typescript
// BAD: Missing return type
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price, 0);
}

// GOOD: Explicit return type
function calculateTotal(items: Item[]): number {
  return items.reduce((sum, item) => sum + item.price, 0);
}

// BAD: Async without type
async function fetchUser(id) {
  return await api.get(`/users/${id}`);
}

// GOOD: Typed async
async function fetchUser(id: string): Promise<User> {
  return await api.get<User>(`/users/${id}`);
}
```

### Medium: Generic Constraints
| Pattern | Issue | Severity |
|---------|-------|----------|
| Unconstrained generics | Too permissive | MEDIUM |
| `T extends any` | Useless constraint | LOW |
| Missing defaults | Poor ergonomics | LOW |

```typescript
// BAD: Unconstrained generic
function getValue<T>(obj: T, key: string) {
  return obj[key];  // Error: Type 'string' cannot be used to index type 'T'
}

// GOOD: Constrained generic
function getValue<T extends Record<string, unknown>, K extends keyof T>(
  obj: T,
  key: K
): T[K] {
  return obj[key];
}

// BAD: No default
interface Props<T> {
  data: T;
}

// GOOD: With default
interface Props<T = unknown> {
  data: T;
}
```

### Low: Type vs Interface
| Pattern | Recommendation | Severity |
|---------|----------------|----------|
| Inconsistent usage | Pick one convention | LOW |
| Type for object shapes | Consider interface | LOW |
| Interface for union | Use type alias | LOW |

```typescript
// Interface: Object shapes, extendable
interface User {
  id: string;
  name: string;
}

interface Admin extends User {
  permissions: string[];
}

// Type: Unions, intersections, primitives
type Status = 'active' | 'inactive' | 'pending';
type UserOrAdmin = User | Admin;
type Point = { x: number; y: number };
```

### tsconfig.json Review
| Option | Recommended | Severity |
|--------|-------------|----------|
| `strict: false` | Enable strict mode | CRITICAL |
| `noImplicitAny: false` | Enable | HIGH |
| `strictNullChecks: false` | Enable | HIGH |
| `useUnknownInCatchVariables: false` | Enable (catch as unknown) | HIGH |
| `noImplicitReturns: false` | Enable | HIGH |
| `forceConsistentCasingInFileNames: false` | Enable (cross-platform) | HIGH |
| `noUncheckedIndexedAccess: false` | Consider enabling | MEDIUM |
| `noFallthroughCasesInSwitch: false` | Enable | MEDIUM |
| `noImplicitOverride: false` | Enable for OOP | MEDIUM |
| `exactOptionalPropertyTypes: false` | Consider for libraries | LOW |
| `skipLibCheck: true` (for libs) | Set false for libraries | MEDIUM |

**Library-specific options:**
| Option | Recommended | Purpose |
|--------|-------------|---------|
| `declaration: true` | Required | Generate .d.ts |
| `declarationMap: true` | Recommended | Source maps for types |
| `composite: true` | For monorepos | Project references |

```json
// RECOMMENDED tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}
```

### Utility Types Usage
| Pattern | Better Alternative | Severity |
|---------|-------------------|----------|
| Manual partial | `Partial<T>` | LOW |
| Manual readonly | `Readonly<T>` | LOW |
| Manual pick | `Pick<T, K>` | LOW |
| Manual omit | `Omit<T, K>` | LOW |

```typescript
// BAD: Manual partial
interface UpdateUserInput {
  name?: string;
  email?: string;
  age?: number;
}

// GOOD: Utility type
type UpdateUserInput = Partial<User>;

// BAD: Manual readonly
interface Config {
  readonly host: string;
  readonly port: number;
}

// GOOD: Utility type
type Config = Readonly<{
  host: string;
  port: number;
}>;

// Useful patterns
type CreateInput = Omit<User, 'id' | 'createdAt'>;
type PublicUser = Pick<User, 'id' | 'name'>;
type NonNullableUser = Required<User>;
```

## Response Template
```
## TypeScript Review Results

**Project**: [name]
**TypeScript**: 5.x | **Target**: ES2022
**Strict Mode**: [enabled/disabled]

### Type Safety Issues

#### CRITICAL
| File | Line | Issue |
|------|------|-------|
| api.ts | 45 | strict mode disabled in tsconfig |

#### HIGH
| File | Line | Issue |
|------|------|-------|
| utils.ts | 23 | `as any` type assertion |
| service.ts | 67 | Non-null assertion `!` on optional |

#### MEDIUM
| File | Line | Issue |
|------|------|-------|
| handlers.ts | 12 | Missing return type on exported function |
| models.ts | 34 | Implicit `any` in callback |

### any Usage Audit
- **Explicit any**: 5 occurrences
- **as any**: 3 occurrences
- **Implicit any**: 2 occurrences
- **Total**: 10 (recommend: 0)

### Recommendations
1. [ ] Enable strict mode in tsconfig.json
2. [ ] Replace `any` with `unknown` + type guards
3. [ ] Add return types to public API functions
4. [ ] Remove non-null assertions, use optional chaining

### Positive Patterns
- Good use of generics in `Repository<T>`
- Proper discriminated unions in `Result<T, E>`
```

## Best Practices
1. **Strict Mode**: Always enable for new projects
2. **`unknown` over `any`**: Use `unknown` for truly dynamic data
3. **Type Guards**: Custom type guards over assertions
4. **Branded Types**: For type-safe IDs
5. **Const Assertions**: `as const` for literal types
6. **Template Literal Types**: For string patterns

```typescript
// Branded types for IDs
type UserId = string & { readonly brand: unique symbol };
type PostId = string & { readonly brand: unique symbol };

function createUserId(id: string): UserId {
  return id as UserId;
}

// Now type-safe:
function getUser(id: UserId): User { ... }
// getUser(postId);  // Error!
```

## Integration
- `code-reviewer` skill: General quality
- `nextjs-reviewer` skill: React/Next.js specifics
- `security-scanner` skill: Security audit

## Notes
- Based on TypeScript 5.x best practices
- Focuses on type safety over runtime
- Respects existing project conventions
- Compatible with JSX/TSX files

Overview

This skill reviews TypeScript codebases for type safety, compiler configuration, and idiomatic patterns. It detects unsafe constructs like explicit any, unchecked assertions, and ts-ignore usage, and provides actionable recommendations. It is focused on TypeScript language-level issues rather than framework-specific concerns. Use it to harden types, improve tsconfig, and guide migrations to stricter typing.

How this skill works

The reviewer scans project files (.ts/.tsx), tsconfig.json, and package.json to detect TypeScript usage and compiler flags. It flags critical patterns (explicit any, as any, @ts-ignore, blind assertions), audits generics and return types, and evaluates tsconfig for recommended strict flags. Results include categorized issues (CRITICAL/HIGH/MEDIUM/LOW), counts of any-related occurrences, and prioritized recommendations to remediate problems.

When to use it

  • Performing a TypeScript code review or type-safety audit
  • Evaluating a tsconfig.json for strictness and compiler options
  • Auditing migrations from JavaScript to TypeScript
  • Checking third-party data handling and external API validation
  • Before releasing library types or publishing declaration files

Best practices

  • Enable strict mode and key flags: noImplicitAny, strictNullChecks, noImplicitReturns
  • Prefer unknown over any and validate external data with type guards or schemas
  • Avoid blind assertions and ts-ignore; use ts-expect-error with documented justification
  • Add explicit return types for public APIs and typed Promise<T> for async functions
  • Use utility types (Partial, Readonly, Pick, Omit) and branded types for ID safety

Example use cases

  • Audit a repo that contains tsconfig.json and mixed .ts/.tsx files to enable stricter compiler checks
  • Find and replace unsafe any usages and advise on converting to unknown plus validators
  • Review migration PRs from JS to TS to ensure exported functions have explicit types
  • Recommend tsconfig changes for a library to produce declaration files and consistent casing
  • Validate usage of generics, suggest proper constraints and defaults for better ergonomics

FAQ

Does this skill fix code automatically?

It produces concrete recommendations and examples but does not rewrite code; use the suggestions to implement fixes.

Will it handle React- or Node-specific issues?

It focuses on core TypeScript checks. Use framework-specific reviewers for React/Next.js or Node.js backend concerns.