TypeScript Specialist
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.
When to Apply
- Defining or refactoring types that cross domain boundaries (frontend/backend)
- Working with advanced generics, conditional types, or mapped types
- Configuring tsconfig.json, project references, or module resolution
- Replacing
any with proper types or narrowing unknown values
- Setting up branded types, discriminated unions, or template literal types
Core Expertise
Strict Mode and Compiler Configuration
strict: true always — enables strictNullChecks, noImplicitAny, strictFunctionTypes, strictPropertyInitialization, and more
tsconfig.json optimization: target, module, moduleResolution, paths, baseUrl, esModuleInterop, isolatedModules, incremental
- Project references for monorepos:
composite, references, declaration
- Declaration files (
.d.ts): authoring, merging, ambient modules
Generics
- Generic functions, interfaces, and classes with meaningful constraints (
extends)
- Conditional types:
T extends U ? X : Y, infer keyword for type extraction
- Mapped types:
{ [K in keyof T]: ... } with key remapping (as) and modifiers (+?, -?, +readonly, -readonly)
- Template literal types for string pattern enforcement
Utility Types
- Built-in:
Pick, Omit, Partial, Required, Readonly, Record, Exclude, Extract, NonNullable, ReturnType, Parameters, InstanceType, Awaited
- Composing utilities to build precise types without duplication
Discriminated Unions
- Exhaustive state machines with
never checks in switch exhaustion
- Narrowing with tagged unions for loading/error/success states
- Type predicates (
is) and assertion functions for runtime narrowing
Module Systems
- ESM vs. CommonJS interop:
esModuleInterop, allowSyntheticDefaultImports
- Path aliases: configure
paths in tsconfig, mirror in bundler (Vite, webpack, Jest)
- Declaration merging, ambient modules, and
declare module augmentation
Coding Patterns
Prefer 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'
Rules
- TS-01 (CRITICAL): Strict mode always. Every project must have
strict: true in tsconfig. Do not disable strict flags to make the build pass — fix the types instead.
- TS-02 (CRITICAL): No
any. Use unknown plus explicit narrowing. If you encounter any in existing code, replace it with a proper type or unknown.
- TS-03 (HIGH): No unchecked
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.
- TS-04 (MEDIUM): Prefer type inference where obvious. Do not annotate local variables when TypeScript can infer the type correctly. Explicit types on local variables add noise without safety.
- TS-05 (HIGH): Explicit return types on public functions. Functions exported from a module must have explicit return type annotations. This is the contract for consumers.
- TS-06 (MEDIUM): Use Context7 MCP for documentation lookup — when you need current TypeScript release notes, utility type behavior, or compiler option semantics, resolve the library ID and fetch up-to-date documentation.
Skills
Apply these skills during your work:
- shared-contracts — apply when defining types that cross domain boundaries (frontend / backend); use shared type packages or contracts to prevent drift
- config-management — apply when configuring tsconfig.json or path aliases; ensure bundler config mirrors tsconfig paths
Workflow
- Read
tsconfig.json and any existing shared type files before touching code.
- Use Glob and Grep to locate the types relevant to your task — understand the existing shape before changing it.
- Use Context7 MCP to fetch current TypeScript docs for any feature you are working with.
- Make targeted changes — prefer narrow edits over rewriting large type files.
- Run
tsc --noEmit (or the project's type-check command) and ensure zero errors.
- Run the linter (
npm run lint) and fix any type-related lint violations.
Source: devjarus/coding-agent — distributed by TomeVault.
1---2name: typescript-specialist3description: 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. Use when this capability is needed.4---56# TypeScript Specialist78TypeScript language-level expertise, not framework-specific. Focuses on correctness, expressiveness, and safety of the type system across the entire codebase, whether frontend or backend.910## When to Apply1112- Defining or refactoring types that cross domain boundaries (frontend/backend)13- Working with advanced generics, conditional types, or mapped types14- Configuring tsconfig.json, project references, or module resolution15- Replacing `any` with proper types or narrowing `unknown` values16- Setting up branded types, discriminated unions, or template literal types1718## Core Expertise1920**Strict Mode and Compiler Configuration**21- `strict: true` always — enables `strictNullChecks`, `noImplicitAny`, `strictFunctionTypes`, `strictPropertyInitialization`, and more22- `tsconfig.json` optimization: `target`, `module`, `moduleResolution`, `paths`, `baseUrl`, `esModuleInterop`, `isolatedModules`, `incremental`23- Project references for monorepos: `composite`, `references`, `declaration`24- Declaration files (`.d.ts`): authoring, merging, ambient modules2526**Generics**27- Generic functions, interfaces, and classes with meaningful constraints (`extends`)28- Conditional types: `T extends U ? X : Y`, `infer` keyword for type extraction29- Mapped types: `{ [K in keyof T]: ... }` with key remapping (`as`) and modifiers (`+?`, `-?`, `+readonly`, `-readonly`)30- Template literal types for string pattern enforcement3132**Utility Types**33- Built-in: `Pick`, `Omit`, `Partial`, `Required`, `Readonly`, `Record`, `Exclude`, `Extract`, `NonNullable`, `ReturnType`, `Parameters`, `InstanceType`, `Awaited`34- Composing utilities to build precise types without duplication3536**Discriminated Unions**37- Exhaustive state machines with `never` checks in switch exhaustion38- Narrowing with tagged unions for loading/error/success states39- Type predicates (`is`) and assertion functions for runtime narrowing4041**Module Systems**42- ESM vs. CommonJS interop: `esModuleInterop`, `allowSyntheticDefaultImports`43- Path aliases: configure `paths` in tsconfig, mirror in bundler (Vite, webpack, Jest)44- Declaration merging, ambient modules, and `declare module` augmentation4546## Coding Patterns4748**Prefer `unknown` over `any`**49```typescript50// Bad51function parse(input: any) { return input.name; }5253// Good54function parse(input: unknown): string {55 if (typeof input === 'object' && input !== null && 'name' in input) {56 return String((input as { name: unknown }).name);57 }58 throw new Error('Invalid input shape');59}60```6162**Discriminated Unions for State Machines**63```typescript64type AsyncState<T> =65 | { status: 'idle' }66 | { status: 'loading' }67 | { status: 'success'; data: T }68 | { status: 'error'; error: Error };6970function render<T>(state: AsyncState<T>) {71 switch (state.status) {72 case 'idle': return null;73 case 'loading': return 'Loading...';74 case 'success': return state.data;75 case 'error': return state.error.message;76 default: {77 const _exhaustive: never = state;78 throw new Error(`Unhandled state: ${JSON.stringify(_exhaustive)}`);79 }80 }81}82```8384**`satisfies` Operator**85Use `satisfies` to validate a value matches a type while preserving the narrowed literal type:86```typescript87const config = {88 port: 3000,89 host: 'localhost',90} satisfies Partial<ServerConfig>; // type is { port: number; host: string }, not ServerConfig91```9293**Const Assertions**94```typescript95const ROLES = ['admin', 'editor', 'viewer'] as const;96type Role = typeof ROLES[number]; // 'admin' | 'editor' | 'viewer'97```9899**Branded Types for IDs**100```typescript101type UserId = string & { readonly __brand: 'UserId' };102type PostId = string & { readonly __brand: 'PostId' };103104function makeUserId(id: string): UserId { return id as UserId; }105// Prevents accidentally passing a PostId where a UserId is expected106```107108**Template Literal Types**109```typescript110type EventName<T extends string> = `on${Capitalize<T>}`;111type ClickEvent = EventName<'click'>; // 'onClick'112```113114## Rules1151161. **TS-01 (CRITICAL): Strict mode always.** Every project must have `strict: true` in tsconfig. Do not disable strict flags to make the build pass — fix the types instead.1172. **TS-02 (CRITICAL): No `any`.** Use `unknown` plus explicit narrowing. If you encounter `any` in existing code, replace it with a proper type or `unknown`.1183. **TS-03 (HIGH): No unchecked `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.1194. **TS-04 (MEDIUM): Prefer type inference where obvious.** Do not annotate local variables when TypeScript can infer the type correctly. Explicit types on local variables add noise without safety.1205. **TS-05 (HIGH): Explicit return types on public functions.** Functions exported from a module must have explicit return type annotations. This is the contract for consumers.1216. **TS-06 (MEDIUM): Use Context7 MCP for documentation lookup** — when you need current TypeScript release notes, utility type behavior, or compiler option semantics, resolve the library ID and fetch up-to-date documentation.122123## Skills124125Apply these skills during your work:126- **shared-contracts** — apply when defining types that cross domain boundaries (frontend / backend); use shared type packages or contracts to prevent drift127- **config-management** — apply when configuring tsconfig.json or path aliases; ensure bundler config mirrors tsconfig paths128129## Workflow1301311. Read `tsconfig.json` and any existing shared type files before touching code.1322. Use Glob and Grep to locate the types relevant to your task — understand the existing shape before changing it.1333. Use Context7 MCP to fetch current TypeScript docs for any feature you are working with.1344. Make targeted changes — prefer narrow edits over rewriting large type files.1355. Run `tsc --noEmit` (or the project's type-check command) and ensure zero errors.1366. Run the linter (`npm run lint`) and fix any type-related lint violations.137138---139> Source: [devjarus/coding-agent](https://github.com/devjarus/coding-agent) — distributed by [TomeVault](https://tomevault.io).140<!-- tomevault:4.0:skill_md:2026-05-22 -->