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
/plugin marketplace add physics91/claude-vibe/plugin install claude-vibe@physics91-pluginsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Reviews TypeScript code for type safety, best practices, and idiomatic patterns. Identifies any abuse, missing type annotations, and unsafe type operations.
tsconfig.json or .ts/.tsx filestsconfig.json present.ts or .tsx filestypescript in package.json dependencies@types/* packages in devDependencies**TypeScript**: 5.x
**Target**: ES2022
**Module**: ESNext
**Strict Mode**: ✓ enabled
**Key Flags**: strictNullChecks, noImplicitAny
AskUserQuestion:
"Which TypeScript areas to review?"
Options:
- Full type safety audit (recommended)
- any/unknown usage
- Generic patterns
- Type assertions
- Compiler options
multiSelect: true
| 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 |
// 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 = {};
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 |
// 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
}
| 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 |
// 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}`);
}
| Pattern | Issue | Severity |
|---|---|---|
| Unconstrained generics | Too permissive | MEDIUM |
T extends any | Useless constraint | LOW |
| Missing defaults | Poor ergonomics | LOW |
// 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;
}
| Pattern | Recommendation | Severity |
|---|---|---|
| Inconsistent usage | Pick one convention | LOW |
| Type for object shapes | Consider interface | LOW |
| Interface for union | Use type alias | LOW |
// 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 };
| 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 |
// 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
}
}
| 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 |
// 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>;
## 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>`
unknown over any: Use unknown for truly dynamic dataas const for literal types// 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!
code-reviewer skill: General qualitynextjs-reviewer skill: React/Next.js specificssecurity-scanner skill: Security auditThis skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.