Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when reading or writing TypeScript or JavaScript files (.ts, .tsx, .js, tsconfig.json).
.claude/skills/aiskillstore-typescript-best-practices/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | -16% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 36% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 37% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 12% | 0% |
| case-14 | ✗→✓ | ▲ Improved | -5% | 0% |
Follows type-first, functional, and error handling patterns from CLAUDE.md. This skill covers language-specific idioms only.
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.
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:
tstype 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:
tsconst 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:
tstype 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}`); } } }
z.infer<>. Avoid duplicating types and schemas.safeParse for user input where failure is expected; use parse at trust boundaries where invalid data is a bug..extend(), .pick(), .omit(), .merge() for DRY definitions..transform() for data normalization at parse time (trim strings, parse dates).tsimport { 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; }
For advanced type utilities beyond TypeScript builtins, consider type-fest:
Opaque<T, Token> - cleaner branded types than manual & { __brand } patternPartialDeep<T> - recursive partial for nested objectsReadonlyDeep<T> - recursive readonly for immutable dataSetRequired<T, K> / SetOptional<T, K> - targeted field modificationsSimplify<T> - flatten complex intersection types in IDE tooltipstsimport type { Opaque, PartialDeep } from 'type-fest'; type UserId = Opaque<string, 'UserId'>; type UserPatch = PartialDeep<User>;
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 11,048 | 5,298 | -52% | 1 | 1 | 0% | 1,943 | 1,793 | -8% | 0 | 0 | — |
case-06 | pass→pass | 14,709 | 6,033 | -59% | 1 | 1 | 0% | 2,492 | 2,086 | -16% | 0 | 0 | — |
case-02 | pass→pass | 18,062 | 5,670 | -69% | 1 | 1 | 0% | 1,933 | 2,033 | +5% | 0 | 0 | — |
case-03 | pass→pass | 7,896 | 3,933 | -50% | 1 | 1 | 0% | 1,509 | 1,693 | +12% | 0 | 0 | — |
case-04 | pass→pass | 9,038 | 5,115 | -43% | 1 | 1 | 0% | 1,636 | 1,926 | +18% | 0 | 0 | — |
case-05 | pass→pass | 11,364 | 4,981 | -56% | 1 | 1 | 0% | 1,931 | 1,841 | -5% | 0 | 0 | — |
case-07 | fail→pass | 16,509 | 6,969 | -58% | 1 | 1 | 0% | 2,642 | 2,228 | -16% | 0 | 0 | — |
case-08 | pass→pass | 10,028 | 4,382 | -56% | 1 | 1 | 0% | 1,888 | 1,774 | -6% | 0 | 0 | — |
case-09 | pass→pass | 7,896 | 4,494 | -43% | 1 | 1 | 0% | 1,414 | 1,751 | +24% | 0 | 0 | — |
case-10 | pass→pass | 9,906 | 6,449 | -35% | 1 | 1 | 0% | 1,770 | 2,152 | +22% | 0 | 0 | — |
case-11 | fail→pass | 11,165 | 6,450 | -42% | 1 | 1 | 0% | 1,614 | 2,194 | +36% | 0 | 0 | — |
case-12 | fail→pass | 7,671 | 4,769 | -38% | 1 | 1 | 0% | 1,396 | 1,906 | +37% | 0 | 0 | — |
case-13 | fail→pass | 8,190 | 3,573 | -56% | 1 | 1 | 0% | 1,417 | 1,589 | +12% | 0 | 0 | — |
case-14 | fail→pass | 12,054 | 5,652 | -53% | 1 | 1 | 0% | 2,092 | 1,994 | -5% | 0 | 0 | — |
case-15 | pass→pass | 6,494 | 3,127 | -52% | 1 | 1 | 0% | 1,169 | 1,597 | +37% | 0 | 0 | — |
case-16 | pass→pass | 10,366 | 5,424 | -48% | 1 | 1 | 0% | 1,940 | 2,079 | +7% | 0 | 0 | — |
case-17 | pass→pass | 11,625 | 7,218 | -38% | 1 | 1 | 0% | 2,207 | 2,264 | +3% | 0 | 0 | — |
case-18 | fail→fail | 8,708 | 6,979 | -20% | 1 | 1 | 0% | 1,572 | 2,305 | +47% | 0 | 0 | — |
case-19 | pass→pass | 5,132 | 2,779 | -46% | 1 | 1 | 0% | 1,035 | 1,447 | +40% | 0 | 0 | — |
case-20 | pass→pass | 7,835 | 16,991 | +117% | 1 | 1 | 0% | 1,703 | 3,002 | +76% | 0 | 0 | — |
case-21 | pass→pass | 6,704 | 6,323 | -6% | 1 | 1 | 0% | 1,247 | 1,994 | +60% | 0 | 0 | — |
case-22 | pass→pass | 4,964 | 4,640 | -7% | 1 | 1 | 0% | 838 | 1,807 | +116% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +23 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.