From dh
Reviews TypeScript code for strict mode, ESM, type safety, branded types, discriminated unions, and anti-patterns. Activates automatically on tsconfig.json, .ts, or .tsx files.
npx claudepluginhub jamie-bitflight/claude_skills --plugin development-harnessThis skill uses the workspace's default tool permissions.
Stack-specific rules loaded by `dh:code-reviewer` when `tsconfig.json`, `*.ts`, or `*.tsx` files are detected.
Enforces TypeScript type safety rules: strict mode, no implicit any or assertions, discriminated unions, unknown with type guards. For writing, reviewing types, or refactoring TS code.
Enforces TypeScript best practices: strict null handling, type guards, Zod validation, ESLint fixes, discriminated unions. Activates on .ts/.tsx/.js/.jsx files, type errors, ESLint issues.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Stack-specific rules loaded by dh:code-reviewer when tsconfig.json, *.ts, or *.tsx files are detected.
tsconfig.json must enable strict: true — this covers noImplicitAny, strictNullChecks, strictFunctionTypes, and othersexactOptionalPropertyTypes: true is required in new projects — prevents undefined from being assigned to optional propertiesnoUncheckedIndexedAccess: true is required when indexing arrays or records — prevents silent undefined propagation@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 awayany type without an explanatory comment is a blocking findingas SomeType) without runtime validation at the same boundary are a blocking findingunknown is the correct type for values from external sources — validate before narrowing, not afterobject as a type is not meaningful — use Record<string, unknown> or a specific interfacevalue!) without a comment explaining why null is impossible are a blocking findingisLoading, isError, isSuccess as 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 };
UserId and OrderId are both string at runtime — without brands, they are interchangeable to the type checkertype UserId = string & { readonly _brand: "UserId" };
type OrderId = string & { readonly _brand: "OrderId" };
function makeUserId(id: string): UserId {
return id as UserId;
}
require() calls are a blocking finding — use import syntaximport type must be used for type-only imports — prevents runtime errors and improves tree-shakingimport() must be typed with the expected module shapeawait or .then()/.catch()) are a blocking findingPromise.all is required for parallel independent async operations — sequential await in a loop is an anti-pattern when operations are independentawait inside a for loop that processes independent items is a blocking findingas UserType casts on external dataJSON.parse(text) as MyType is a blocking findingprocess.env.API_KEY! without validation is a blocking findingsatisfies Operatorsatisfies is preferred over explicit type annotations for config objects and record literals — preserves the literal type while validating against the declared type// RIGHT: satisfies preserves literal types
const config = {
port: 3000,
host: "localhost",
} satisfies ServerConfig;
// config.port is typed as 3000, not number
// 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);