
Typescript Best Practices
Load consistent type-first TypeScript and JavaScript idioms whenever your agent touches .ts, .tsx, .js, or tsconfig.json.
Overview
TypeScript Best Practices is an agent skill most often used in Build (also Ship review) that encodes type-first, functional TypeScript and JavaScript patterns for agent-assisted editing of .ts, .tsx, .js, and tsconfig.js
Install
npx skills add https://github.com/0xbigboss/claude-code --skill typescript-best-practicesWhat is this skill?
- Discriminated unions and branded types to make illegal states unrepresentable at compile time
- Const assertions and literal unions for exhaustiveness-safe domain modeling
- Explicit pairing rule: load react-best-practices alongside when editing .tsx/.jsx or @react imports
- Type-first and functional error-handling patterns aligned with CLAUDE.md-style agent projects
- Language-only scope—React effects, hooks, and component design stay in the dedicated React skill
Adoption & trust: 2.5k installs on skills.sh; 49 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You want your agent to write TypeScript that prevents invalid states at compile time instead of repeating ad-hoc boolean flags and untyped string IDs across every file.
Who is it for?
Solo builders shipping typed React or Node code with Claude Code, Cursor, or Codex who already care about exhaustive switches and domain-safe IDs.
Skip if: Teams that only need React-specific guidance without TS fundamentals, or projects with no TypeScript/JavaScript in the repo.
When should I use this skill?
Use when reading or writing TypeScript or JavaScript files (.ts, .tsx, .js, tsconfig.json).
What do I get? / Deliverables
Edited TypeScript follows discriminated unions, branded primitives, and const-asserted unions, with React work explicitly delegated to react-best-practices when components are in scope.
- Type-safe refactors and new code following documented TS idioms
- Explicit handoff to react-best-practices when React files are edited
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Canonical shelf is Build because the skill activates during implementation and refactoring of typed application code, which is where solo builders spend most TS editing time. Frontend is the primary shelf: React/TSX and UI-heavy solo stacks are the documented pairing path, even though the patterns also apply to Node and shared libraries.
Where it fits
Refactor a fetch hook to a discriminated union RequestState so loading and error cannot coexist.
Brand external IDs in an API client so OrderId cannot be passed to getUser.
Review a PR diff in .ts files for boolean-flag state machines that should be union types.
How it compares
Use as a language-pattern companion to react-best-practices, not as a substitute for framework-level React rules.
Common Questions / FAQ
Who is typescript-best-practices for?
It is for indie developers and small teams using AI coding agents on TypeScript or JavaScript codebases who want compile-time safety patterns applied consistently during reads and writes.
When should I use typescript-best-practices?
Use it in Build while implementing or refactoring modules, in Ship during code review of typed files, and whenever you touch tsconfig.json; pair react-best-practices when working on .tsx or React imports.
Is typescript-best-practices safe to install?
It is documentation-style procedural knowledge with no bundled network or shell requirements in SKILL.md; review the Security Audits panel on this Prism page before trusting any third-party skill package.
SKILL.md
READMESKILL.md - Typescript Best Practices
# TypeScript Best Practices Follows type-first, functional, and error handling patterns from CLAUDE.md. This skill covers language-specific idioms only. ## Pair with React Best Practices When working with React components (`.tsx`, `.jsx` files or `@react` imports), always load `react-best-practices` alongside this skill. This skill covers TypeScript fundamentals; React-specific patterns (effects, hooks, refs, component design) are in the dedicated React skill. ## Make Illegal States Unrepresentable Use the type system to prevent invalid states at compile time. **Discriminated unions for mutually exclusive states:** ```ts // Good: only valid combinations possible type RequestState<T> = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: T } | { status: 'error'; error: Error }; // Bad: allows invalid combinations like { loading: true, error: Error } type RequestState<T> = { loading: boolean; data?: T; error?: Error; }; ``` **Branded types for domain primitives:** ```ts type UserId = string & { readonly __brand: 'UserId' }; type OrderId = string & { readonly __brand: 'OrderId' }; // Compiler prevents passing OrderId where UserId expected function getUser(id: UserId): Promise<User> { /* ... */ } ``` **Const assertions for literal unions:** ```ts const ROLES = ['admin', 'user', 'guest'] as const; type Role = typeof ROLES[number]; // 'admin' | 'user' | 'guest' // Array and type stay in sync automatically function isValidRole(role: string): role is Role { return ROLES.includes(role as Role); } ``` **Exhaustive switch with never check:** ```ts type Status = "active" | "inactive"; function processStatus(status: Status): string { switch (status) { case "active": return "processing"; case "inactive": return "skipped"; default: { const _exhaustive: never = status; throw new Error(`unhandled status: ${_exhaustive}`); } } } ``` ## Runtime Validation with Zod - Define schemas as single source of truth; infer TypeScript types with `z.infer<>`. Avoid duplicating types and schemas. - Use `safeParse` for user input where failure is expected; use `parse` at trust boundaries where invalid data is a bug. - Compose schemas with `.extend()`, `.pick()`, `.omit()`, `.merge()` for DRY definitions. - Add `.transform()` for data normalization at parse time (trim strings, parse dates). ```ts import { z } from "zod"; const UserSchema = z.object({ id: z.string().uuid(), email: z.string().email(), name: z.string().min(1), createdAt: z.string().transform((s) => new Date(s)), }); type User = z.infer<typeof UserSchema>; // Strict parsing at trust boundaries — throws if API contract violated export async function fetchUser(id: string): Promise<User> { const response = await fetch(`/api/users/${id}`); if (!response.ok) { throw new Error(`fetch user ${id} failed: ${response.status}`); } return UserSchema.parse(await response.json()); } // Caller handles both success and error from user input const result = UserSchema.safeParse(formData); if (!result.success) { setErrors(result.error.flatten().fieldErrors); return; } ``` ## Optional: type-fest For advanced type utilities beyond TypeScript builtins, consider [type-fest](https://github.com/sindresorhus/type-fest): - `Opaque<T, Token>` - cleaner branded types than manual `& { __brand }` pattern - `PartialDeep<T>` - recursive partial for nested objects - `ReadonlyDeep<T>` - recursive readonly for immutable data - `SetRequired<T, K>` / `SetOptional<T, K>` - targeted field modifications - `Simplify<T>` - flatten complex intersection types in IDE tooltips ```ts import type { Opaque, PartialDeep } from 'type-fest'; type UserId = Opaque<string, 'UserId'>; type UserPatch = PartialDeep<User>; ```