Back to list
physics91

typescript-reviewer

by physics91

Claude Code hooks를 활용하여 세션 컴팩션 시 AGENTS.md 컨텍스트를 자동으로 보존/복원하는 플러그인

0🍴 0📅 Dec 29, 2025

SKILL.md


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

PatternIssueSeverity
Explicit anyBypasses type systemCRITICAL
as any castingType escape hatchCRITICAL
// @ts-ignoreSilences all errorsCRITICAL
// @ts-nocheckDisables file checkingCRITICAL
any[] arrayUntyped collectionsHIGH
Function returns anyPropagates unsafetyHIGH
Promise<any>Async unsafetyHIGH
Function typeToo broadHIGH
Object / {} typeAlmost anythingHIGH
Record<string, any>Indexed unsafetyHIGH
// 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

PatternIssueSeverity
as Type on external dataNo runtime validationCRITICAL
as unknown as TypeDouble assertionHIGH
as Type on internal dataUnsafe assumptionMEDIUM
<Type>value (JSX conflict)Legacy syntaxLOW
! non-null assertionRuntime error riskHIGH
! after guard/assertionAcceptableOK
// 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

PatternIssueSeverity
No function return typeInferred may be wrongMEDIUM
Public API without typesPoor documentationHIGH
Async without PromiseReturn type unclearMEDIUM
// 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

PatternIssueSeverity
Unconstrained genericsToo permissiveMEDIUM
T extends anyUseless constraintLOW
Missing defaultsPoor ergonomicsLOW
// 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

PatternRecommendationSeverity
Inconsistent usagePick one conventionLOW
Type for object shapesConsider interfaceLOW
Interface for unionUse type aliasLOW
// 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

OptionRecommendedSeverity
strict: falseEnable strict modeCRITICAL
noImplicitAny: falseEnableHIGH
strictNullChecks: falseEnableHIGH
useUnknownInCatchVariables: falseEnable (catch as unknown)HIGH
noImplicitReturns: falseEnableHIGH
forceConsistentCasingInFileNames: falseEnable (cross-platform)HIGH
noUncheckedIndexedAccess: falseConsider enablingMEDIUM
noFallthroughCasesInSwitch: falseEnableMEDIUM
noImplicitOverride: falseEnable for OOPMEDIUM
exactOptionalPropertyTypes: falseConsider for librariesLOW
skipLibCheck: true (for libs)Set false for librariesMEDIUM

Library-specific options:

OptionRecommendedPurpose
declaration: trueRequiredGenerate .d.ts
declarationMap: trueRecommendedSource maps for types
composite: trueFor monoreposProject references
// 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

PatternBetter AlternativeSeverity
Manual partialPartial<T>LOW
Manual readonlyReadonly<T>LOW
Manual pickPick<T, K>LOW
Manual omitOmit<T, K>LOW
// 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
// 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

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon