Provides TypeScript-specific code review patterns covering strict mode, ESM, type safety, branded types, discriminated unions, async patterns, runtime safety, and common anti-patterns. Activates on detection of tsconfig.json, *.ts, or *.tsx files during code review — loaded automatically by dh:code-reviewer.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add Jamie-BitFlight/claude_skills --skill code-review-typescript --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Code Review Typescript?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jamie-bitflight-code-review-typescript)More formats (shields.io, HTML) on the badges page.
---
name: code-review-typescript
description: Provides TypeScript-specific code review patterns covering strict mode, ESM, type safety, branded types, discriminated unions, async patterns, runtime safety, and common anti-patterns. Activates on detection of tsconfig.json, *.ts, or *.tsx files during code review — loaded automatically by dh:code-reviewer.
user-invocable: false
---
# TypeScript Code Review Patterns
Stack-specific rules loaded by `dh:code-reviewer` when `tsconfig.json`, `*.ts`, or `*.tsx` files are detected.
## Strict Mode
- `tsconfig.json` must enable `strict: true` — this covers `noImplicitAny`, `strictNullChecks`, `strictFunctionTypes`, and others
- `exactOptionalPropertyTypes: true` is required in new projects — prevents `undefined` from being assigned to optional properties
- `noUncheckedIndexedAccess: true` is required when indexing arrays or records — prevents silent `undefined` propagation
- Any `@ts-ignore` comment without an accompanying explanation comment is a blocking finding
- `@ts-expect-error` is preferred over `@ts-ignore` — it fails if the error goes away
## Type Safety
- `any` type without an explanatory comment is a blocking finding
- Type assertions (`as SomeType`) without runtime validation at the same boundary are a blocking finding
- `unknown` is the correct type for values from external sources — validate before narrowing, not after
- `object` as a type is not meaningful — use `Record<string, unknown>` or a specific interface
- Non-null assertions (`value!`) without a comment explaining why null is impossible are a blocking finding
## Discriminated Unions Over Booleans
- Multiple boolean flags encoding state are a blocking finding — model as a discriminated union instead
- State machine logic with `isLoading`, `isError`, `isSuccess` as separate booleans allows impossible combinations
```typescript
// WRONG: boolean flags allow impossible states
interface State {
isLoading: boolean;
isError: boolean;
data: User | null;
}
// RIGHT: discriminated union — impossible states are unrepresentable
type State =
| { status: "loading" }
| { status: "error"; error: Error }
| { status: "success"; data: User };
```
## Branded Types
- Domain primitives that are structurally identical but semantically distinct must use branded types to prevent mix-ups
- `UserId` and `OrderId` are both `string` at runtime — without brands, they are interchangeable to the type checker
```typescript
type UserId = string & { readonly _brand: "UserId" };
type OrderId = string & { readonly _brand: "OrderId" };
function makeUserId(id: string): UserId {
return id as UserId;
}
```
## ESM
- `require()` calls are a blocking finding — use `import` syntax
- Named exports are preferred over default exports — easier to refactor and search
- `import type` must be used for type-only imports — prevents runtime errors and improves tree-shaking
- Dynamic `import()` must be typed with the expected module shape
## Async Patterns
- Floating promises (calling an async function without `await` or `.then()/.catch()`) are a blocking finding
- `Promise.all` is required for parallel independent async operations — sequential `await` in a loop is an anti-pattern when operations are independent
- `await` inside a `for` loop that processes independent items is a blocking finding
- Unhandled promise rejections must have explicit error handling at the call site
## Runtime Safety
- User-controlled input entering the system must be validated against a schema (zod, valibot, or equivalent) at the boundary — no raw `as UserType` casts on external data
- JSON.parse results must be validated before use — `JSON.parse(text) as MyType` is a blocking finding
- Environment variables must be validated at startup with specific error messages — `process.env.API_KEY!` without validation is a blocking finding
## `satisfies` Operator
- `satisfies` is preferred over explicit type annotations for config objects and record literals — preserves the literal type while validating against the declared type
- Use when you want both type checking AND the narrowed type available downstream
```typescript
// RIGHT: satisfies preserves literal types
const config = {
port: 3000,
host: "localhost",
} satisfies ServerConfig;
// config.port is typed as 3000, not number
```
## Anti-Patterns
```typescript
// WRONG: any without comment
function process(data: any) { ... }
// RIGHT: specific type or documented any
function process(data: unknown) {
if (!isUserEvent(data)) throw new TypeError("Expected UserEvent");
...
}
// WRONG: floating promise
sendMetrics(event);
// RIGHT: awaited or explicitly fire-and-forget
void sendMetrics(event); // intentionally not awaited — best-effort telemetry
// or
await sendMetrics(event);
```
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!