skills/backend/typescript-specialist/SKILL.md
TypeScript expertise — language-level patterns for strict typing, generics, utility types, module systems, and TypeScript configuration. Use for cross-cutting type system work that spans frontend and backend.
npx skillsauth add devjarus/coding-agent typescript-specialistInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
TypeScript language-level expertise, not framework-specific. Focuses on correctness, expressiveness, and safety of the type system across the entire codebase, whether frontend or backend.
any with proper types or narrowing unknown valuesStrict Mode and Compiler Configuration
strict: true always — enables strictNullChecks, noImplicitAny, strictFunctionTypes, strictPropertyInitialization, and moretsconfig.json optimization: target, module, moduleResolution, paths, baseUrl, esModuleInterop, isolatedModules, incrementalcomposite, references, declaration.d.ts): authoring, merging, ambient modulesGenerics
extends)T extends U ? X : Y, infer keyword for type extraction{ [K in keyof T]: ... } with key remapping (as) and modifiers (+?, -?, +readonly, -readonly)Utility Types
Pick, Omit, Partial, Required, Readonly, Record, Exclude, Extract, NonNullable, ReturnType, Parameters, InstanceType, AwaitedDiscriminated Unions
never checks in switch exhaustionis) and assertion functions for runtime narrowingModule Systems
esModuleInterop, allowSyntheticDefaultImportspaths in tsconfig, mirror in bundler (Vite, webpack, Jest)declare module augmentationPrefer unknown over any
// Bad
function parse(input: any) { return input.name; }
// Good
function parse(input: unknown): string {
if (typeof input === 'object' && input !== null && 'name' in input) {
return String((input as { name: unknown }).name);
}
throw new Error('Invalid input shape');
}
Discriminated Unions for State Machines
type AsyncState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error };
function render<T>(state: AsyncState<T>) {
switch (state.status) {
case 'idle': return null;
case 'loading': return 'Loading...';
case 'success': return state.data;
case 'error': return state.error.message;
default: {
const _exhaustive: never = state;
throw new Error(`Unhandled state: ${JSON.stringify(_exhaustive)}`);
}
}
}
satisfies Operator
Use satisfies to validate a value matches a type while preserving the narrowed literal type:
const config = {
port: 3000,
host: 'localhost',
} satisfies Partial<ServerConfig>; // type is { port: number; host: string }, not ServerConfig
Const Assertions
const ROLES = ['admin', 'editor', 'viewer'] as const;
type Role = typeof ROLES[number]; // 'admin' | 'editor' | 'viewer'
Branded Types for IDs
type UserId = string & { readonly __brand: 'UserId' };
type PostId = string & { readonly __brand: 'PostId' };
function makeUserId(id: string): UserId { return id as UserId; }
// Prevents accidentally passing a PostId where a UserId is expected
Template Literal Types
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<'click'>; // 'onClick'
strict: true in tsconfig. Do not disable strict flags to make the build pass — fix the types instead.any. Use unknown plus explicit narrowing. If you encounter any in existing code, replace it with a proper type or unknown.as casts. Only use type assertions when you have a runtime check immediately before that guarantees the shape. Add a comment explaining why the cast is safe.Apply these skills during your work:
tsconfig.json and any existing shared type files before touching code.tsc --noEmit (or the project's type-check command) and ensure zero errors.npm run lint) and fix any type-related lint violations.testing
Multi-source research method — decompose a question, fan out parallel investigators, interleaved-think each result, verify claims adversarially, synthesize a cited answer. Use for breadth-heavy research, stack comparisons, "which approach wins" questions.
testing
Decide when to use unit vs integration vs e2e tests, and when to mock vs use the real thing per dependency. Dependency injection is the enabler — without it you end up monkey-patching imports. Apply when writing tests of any kind.
development
Test-driven development process — write failing test, implement to pass, refactor. Use when implementing any feature or fixing bugs.
development
Patterns for sharing types, API contracts, and validation schemas between frontend and backend. Use when multiple domains consume the same data shapes to prevent contract drift.