From ts-dev-kit
Enforces TypeScript conventions for strict, type-safe code: no `any` use unknown, interfaces vs types, literal unions over enums, discriminated unions, type narrowing with guards, branded types, and anti-pattern avoidance.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ts-dev-kit:typescript-conventionsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Project-wide TypeScript standards that complement agent-specific instructions.
Project-wide TypeScript standards that complement agent-specific instructions.
any: Use unknown if the type is truly dynamic, then narrow.strict: true, noUncheckedIndexedAccess, verbatimModuleSyntax.Readonly<T>, Pick, Omit, and Record for precise types.UserId, OrderId) to prevent mixing.z.infer<typeof schema> over hand-written types when a Zod schema exists.extends and declaration merging.interface for plain objects, type for everything else.// Interface: object shape, extensible
interface User {
id: string;
name: string;
}
interface Employee extends User {
company: string;
}
// Type: union, intersection, computed
type Result = Success | Failure;
type UserProfile = User & { bio: string };
type Nullable<T> = { [K in keyof T]: T[K] | null };
// Prefer this
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type Direction = "north" | "south" | "east" | "west";
// Over this (emits runtime JS)
enum HttpMethod { GET, POST, PUT, DELETE }
Add a type (or kind) literal field to each variant. Always handle exhaustiveness with assertNever.
interface Circle { type: "circle"; radius: number }
interface Square { type: "square"; side: number }
interface Triangle { type: "triangle"; base: number; height: number }
type Shape = Circle | Square | Triangle;
function assertNever(x: never): never {
throw new Error(`Unexpected value: ${x}`);
}
function area(shape: Shape): number {
switch (shape.type) {
case "circle": return Math.PI * shape.radius ** 2;
case "square": return shape.side ** 2;
case "triangle": return (shape.base * shape.height) / 2;
default: return assertNever(shape);
}
}
Always narrow before accessing type-specific properties.
typeof for primitives: typeof x === "string"in for object shapes: "swim" in petfunction isBook(item): item is Bookfunction format(input: string | number): string {
if (typeof input === "string") return input.toUpperCase();
return input.toFixed(2);
}
// Custom type guard
function isError(result: Result): result is ErrorResult {
return result.success === false;
}
extends — never assume properties exist on unconstrained T.T = unknown) when callers often use a single type.// Constrained generic
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// With default
type ApiResponse<T = unknown> = { data: T; status: number };
Use mapped types to derive variants from a base — never duplicate type definitions.
// All fields optional (equivalent to built-in Partial<T>)
type Optional<T> = { [K in keyof T]?: T[K] };
// All fields nullable
type Nullable<T> = { [K in keyof T]: T[K] | null };
// Key remapping with template literals
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
& to compose smaller interfaces into richer types.never — check for this.type Timestamped = { createdAt: Date; updatedAt: Date };
type UserRecord = User & Timestamped;
// Type-only imports (required by verbatimModuleSyntax)
import type { FastifyInstance } from "fastify";
// Mixed imports: separate values and types
import { z } from "zod/v4";
import type { ZodType } from "zod/v4";
// ioredis: always named import
import { Redis } from "ioredis";
httpErrors or use reply.status().send() — the centralized setErrorHandler formats the response.NotFoundError, ConflictError).getUserById, createReport, isActive, hasPermissionis/has/can/should prefixget, find, list, fetchcreate, update, delete, add, remove<anti_patterns>
RATE_LIMITS, PAGINATION, CACHE)..length on string | number fails at runtime if it's a number.readonly is shallow: readonly prevents reassignment but doesn't freeze nested objects.<T> with no extends loses type info — constrain or use a concrete type.{ status: string } & { status: number } silently collapses to never.default: return assertNever(x) in discriminated union switches.</anti_patterns>
npx claudepluginhub jgamaraalv/ts-dev-kit --plugin ts-dev-kitTypeScript type system, strict mode, and TS-specific patterns beyond JavaScript fundamentals. Invoke whenever task involves any interaction with TypeScript code — writing, reviewing, refactoring, debugging .ts/.tsx files, type definitions, generics, narrowing, tsconfig, or type-level programming.
Guides TypeScript code with pedantic best practices: strict tsconfig flags like noUncheckedIndexedAccess, discriminated unions over assertions, Zod runtime validation, barrel exports, as const, ESLint rules.
Enforces TypeScript type safety with strict mode, no `any`, and discriminated unions. Use when writing or reviewing TypeScript code.