TypeScript Code Review Patterns
SkillFiles & storageProvides 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.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the TypeScript Code Review Patterns skill
What this skill tells your AI
The instructions your AI receives, as published by jamie-bitflight/claude_skills in plugins/development-harness/skills/code-review-typescript/SKILL.md and read by ahel’s review.
Stack-specific rules loaded by dh:code-reviewer when tsconfig.json, *.ts, or *.tsx files are detected.
Strict Mode
tsconfig.jsonmust enablestrict: true— this coversnoImplicitAny,strictNullChecks,strictFunctionTypes, and othersexactOptionalPropertyTypes: trueis required in new projects — preventsundefinedfrom being assigned to optional propertiesnoUncheckedIndexedAccess: trueis required when indexing arrays or records — prevents silentundefinedpropagation- Any
@ts-ignorecomment without an accompanying explanation comment is a blocking finding @ts-expect-erroris preferred over@ts-ignore— it fails if the error goes away
Type Safety
anytype without an explanatory comment is a blocking finding- Type assertions (
as SomeType) without runtime validation at the same boundary are a blocking finding unknownis the correct type for values from external sources — validate before narrowing, not afterobjectas a type is not meaningful — useRecord<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,isSuccessas separate booleans allows impossible combinations
// 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
UserIdandOrderIdare bothstringat runtime — without brands, they are interchangeable to the type checker
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 — useimportsyntax- Named exports are preferred over default exports — easier to refactor and search
import typemust 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
awaitor.then()/.catch()) are a blocking finding Promise.allis required for parallel independent async operations — sequentialawaitin a loop is an anti-pattern when operations are independentawaitinside aforloop 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 UserTypecasts on external data - JSON.parse results must be validated before use —
JSON.parse(text) as MyTypeis 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
satisfiesis 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
// RIGHT: satisfies preserves literal types
const config = {
port: 3000,
host: "localhost",
} satisfies ServerConfig;
// config.port is typed as 3000, not number
Anti-Patterns
// 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);
Signals
- GitHub stars
- 66
- Forks
- 10
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
code-review-typescript- Source
- github.com/jamie-bitflight/claude_skills