JavaScript & TypeScript
Primary reference: TypeScript docs. Rule files below link to the specific docs section they encode; when a rule and the official docs disagree, the docs win and the rule should be updated.
Route to Sub-skills
type-system/ and zod/ are reference bundles read on demand from this router — they are not independently discovered skills, so their own frontmatter is intentionally minimal (no duplicate trigger list needed).
→ Type system (unknown/any, narrowing, discriminated unions, mapped types…) → type-system/SKILL.md
→ Zod (schema validation, transforms, coercion, branded types…) → zod/SKILL.md
→ Functional composition (pipe/compose, currying, pure functions, factories, functional mixins, monoids…) → composition/SKILL.md
→ Design patterns (Strategy, Factory, Builder, Decorator, Mixin…) → object-oriented-programming skill
→ SOLID principles (SRP, OCP, LSP, ISP, DIP) → object-oriented-programming skill
→ Testing (unit tests, mocks vs stubs, brittle tests, test pyramid) → testing skill
Error Handling
| Strategy |
Caller forced to handle? |
Composability |
Return T | null |
Yes (null check) |
Low |
| Throw exception |
No — easy to miss |
High |
Return exception T | ErrorA | ErrorB |
Yes — union exhaustion |
Medium |
| Option/Either type |
Via .flatMap chain |
High (needs library) |
Return exceptions (preferred for expected failures):
class BadRequestError extends Error {
readonly status = 400 as const;
}
class UnauthorizedError extends Error {
readonly status = 401 as const;
}
class NotFoundError extends Error {
readonly status = 404 as const;
}
function resolveUser(
token: string,
id: string,
): User | BadRequestError | UnauthorizedError | NotFoundError {
if (!id.trim()) return new BadRequestError("Missing user ID");
if (!isValidJwt(token)) return new UnauthorizedError("Invalid token");
const user = userStore.get(id);
if (!user) return new NotFoundError(`User ${id} not found`);
return user;
}
const result = resolveUser(authHeader, userId);
if (result instanceof BadRequestError) res.status(400).send(result.message);
else if (result instanceof UnauthorizedError)
res.status(401).send(result.message);
else if (result instanceof NotFoundError) res.status(404).send(result.message);
else res.status(200).json(result);
TypeScript at Scale
- Enable
"strict": true globally; enforce in CI (Compiler Options); layer on the flags not included in strict: noUncheckedIndexedAccess, noImplicitReturns, noFallthroughCasesInSwitch, noUnusedLocals, noUnusedParameters
- Use
@ts-expect-error over @ts-ignore
- Track
any usage via @typescript-eslint/no-explicit-any
- Keep API/DTO types separate from domain types — map at boundaries (example below)
- Validate external inputs (API bodies, env vars, queues) with Zod at boundaries (example:
zod/SKILL.md)
- Publish domain contracts as
@org/contracts; use project references for boundaries (Project References)
Read On Demand
- Domain vs. DTO mapping example — DTO shape never leaks into the domain:
export type UserDTO = {
user_id: string;
display_name: string;
created_at: string;
};
export type User = { id: string; name: string; createdAt: Date };
export function toDomain(dto: UserDTO): User {
return {
id: dto.user_id,
name: dto.display_name,
createdAt: new Date(dto.created_at),
};
}
- Zod boundary-validation example:
zod/SKILL.md.
- Node.js runtime topics (event loop/libuv, core modules, CJS/ESM, debugging & profiling, HTTP ecosystem, npm/packaging, security): see the free ebook "Become a Node.js developer" (online). Note: French edition is complete; English edition is partial.
- ECMAScript edition history (ES1 1997 → ES2025): see MDN's JavaScript editions timeline. Use when judging which edition first shipped a feature, what needs a polyfill on older runtimes, which syntax is safe for a target environment, or choosing
tsconfig target/lib.
Rules (JavaScript & TypeScript, always apply)
JavaScript foundation
| Rule |
File |
| Use JavaScript general conventions (naming, const/let, destructuring, template literals) |
rules/js-general-conventions.md |
Prefer explicit context (params) over implicit this |
rules/prefer-explicit-context-over-this.md |
Do not use barrel files (index.js/index.ts re-exports) |
rules/no-barrel-files.md |
Avoid intermediate arrays on hot paths (filter().map() chains) |
rules/avoid-intermediate-arrays.md |
undefined for absence, null for API/external contracts |
rules/null-undefined.md |
TypeScript-specific
| Rule |
File |
Avoid type assertions (as T, !, as unknown as T) |
rules/avoid-type-assertions.md |
Favor existing types over as const |
rules/favor-existing-types-over-as-const.md |
Do not prefix interfaces with I |
rules/no-interface-prefix.md |
Mark properties and arrays readonly to signal immutability |
rules/readonly.md |
Annotate function return types explicitly; enable noImplicitAny |
rules/explicit-return-types.md |
| Use modules instead of namespaces; prefer named exports |
rules/module-organization.md |
Prefer shipped types / @types/*; otherwise add a minimal .d.ts |
rules/js-interop-declarations.md |
Benchmark
This router has no scenario of its own. Gate data lives in the leaf footers:
type-system/SKILL.md → ## Benchmark (scenarios typescript-001 PASS and typescript-002 SOFT PASS, run 2026-08-31).
composition/SKILL.md → ## Benchmark (scenario composing-software-001, run 2026-08-31, PASS).
- Historical optimizer runs:
run-history.md (includes per-skill gate targets).
Gate per .agents/skills/skill-optimizer/rules/release-gates.md.
1---2name: typescript3description: JavaScript & TypeScript best-practices and rule enforcement — JS idioms (naming, `this`-handling, module structure, nullability, iteration performance) and TS-specific type safety, runtime validation, and error handling. Routes to type-system and Zod sub-skills and `object-oriented-programming` for design patterns and SOLID. TRIGGER when: language (TypeScript, TS, .ts, .tsx, JavaScript, JS, .js, .mjs, .cjs, Node.js, browser JS, ESM, CommonJS), type-system (discriminated unions, generics, utility types, make illegal states unrepresentable, type narrowing, variance, contravariance), safety (strict mode, any vs unknown, ts-expect-error, ts-ignore, type assertions, noUncheckedIndexedAccess, noImplicitReturns), runtime (Zod, schema validation, runtime type checks, parse/safeParse), errors (error handling without throwing, union return errors, Result type), ts-conventions (readonly modifier, return type annotations, module organization, namespace, export default, interface prefix, `.d.ts`, ambient declaration, decl4---56# JavaScript & TypeScript78Primary reference: [TypeScript docs](https://www.typescriptlang.org/docs/). Rule files below link to the specific docs section they encode; when a rule and the official docs disagree, the docs win and the rule should be updated.910## Route to Sub-skills1112`type-system/` and `zod/` are reference bundles read on demand from this router — they are not independently discovered skills, so their own frontmatter is intentionally minimal (no duplicate trigger list needed).1314→ **Type system** (unknown/any, narrowing, discriminated unions, mapped types…) → `type-system/SKILL.md`15→ **Zod** (schema validation, transforms, coercion, branded types…) → `zod/SKILL.md`16→ **Functional composition** (pipe/compose, currying, pure functions, factories, functional mixins, monoids…) → `composition/SKILL.md`17→ **Design patterns** (Strategy, Factory, Builder, Decorator, Mixin…) → `object-oriented-programming` skill18→ **SOLID principles** (SRP, OCP, LSP, ISP, DIP) → `object-oriented-programming` skill19→ **Testing** (unit tests, mocks vs stubs, brittle tests, test pyramid) → `testing` skill2021---2223## Error Handling2425| Strategy | Caller forced to handle? | Composability |26| -------------------------------------------- | -------------------------- | -------------------- |27| Return `T \| null` | Yes (null check) | Low |28| Throw exception | No — easy to miss | High |29| **Return exception** `T \| ErrorA \| ErrorB` | **Yes — union exhaustion** | Medium |30| Option/Either type | Via `.flatMap` chain | High (needs library) |3132**Return exceptions (preferred for expected failures):**3334```typescript35class BadRequestError extends Error {36 readonly status = 400 as const;37}38class UnauthorizedError extends Error {39 readonly status = 401 as const;40}41class NotFoundError extends Error {42 readonly status = 404 as const;43}4445function resolveUser(46 token: string,47 id: string,48): User | BadRequestError | UnauthorizedError | NotFoundError {49 if (!id.trim()) return new BadRequestError("Missing user ID");50 if (!isValidJwt(token)) return new UnauthorizedError("Invalid token");51 const user = userStore.get(id);52 if (!user) return new NotFoundError(`User ${id} not found`);53 return user;54}55```5657```typescript58const result = resolveUser(authHeader, userId);59if (result instanceof BadRequestError) res.status(400).send(result.message);60else if (result instanceof UnauthorizedError)61 res.status(401).send(result.message);62else if (result instanceof NotFoundError) res.status(404).send(result.message);63else res.status(200).json(result);64```6566---6768## TypeScript at Scale69701. Enable `"strict": true` globally; enforce in CI ([Compiler Options](https://www.typescriptlang.org/tsconfig/#strict)); layer on the flags not included in strict: [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig/#noUncheckedIndexedAccess), [`noImplicitReturns`](https://www.typescriptlang.org/tsconfig/#noImplicitReturns), [`noFallthroughCasesInSwitch`](https://www.typescriptlang.org/tsconfig/#noFallthroughCasesInSwitch), [`noUnusedLocals`](https://www.typescriptlang.org/tsconfig/#noUnusedLocals), [`noUnusedParameters`](https://www.typescriptlang.org/tsconfig/#noUnusedParameters)712. Use `@ts-expect-error` over `@ts-ignore`723. Track `any` usage via `@typescript-eslint/no-explicit-any`734. Keep API/DTO types separate from domain types — map at boundaries (example below)745. Validate external inputs (API bodies, env vars, queues) with Zod at boundaries (example: `zod/SKILL.md`)756. Publish domain contracts as `@org/contracts`; use project references for boundaries ([Project References](https://www.typescriptlang.org/docs/handbook/project-references.html))7677## Read On Demand7879- Domain vs. DTO mapping example — DTO shape never leaks into the domain:8081```typescript82export type UserDTO = {83 user_id: string;84 display_name: string;85 created_at: string;86};8788export type User = { id: string; name: string; createdAt: Date };8990export function toDomain(dto: UserDTO): User {91 return {92 id: dto.user_id,93 name: dto.display_name,94 createdAt: new Date(dto.created_at),95 };96}97```9899- Zod boundary-validation example: `zod/SKILL.md`.100- Node.js runtime topics (event loop/libuv, core modules, CJS/ESM, debugging & profiling, HTTP ecosystem, npm/packaging, security): see the free ebook ["Become a Node.js developer"](https://github.com/fraxken/ebook_nodejs) ([online](https://fraxken.github.io/ebook_nodejs/)). Note: French edition is complete; English edition is partial.101- ECMAScript edition history (ES1 1997 → ES2025): see [MDN's JavaScript editions timeline](https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript). Use when judging which edition first shipped a feature, what needs a polyfill on older runtimes, which syntax is safe for a target environment, or choosing `tsconfig` `target`/`lib`.102103## Rules (JavaScript & TypeScript, always apply)104105### JavaScript foundation106107| Rule | File |108| ---------------------------------------------------------------------------------------- | -------------------------------------------- |109| Use JavaScript general conventions (naming, const/let, destructuring, template literals) | `rules/js-general-conventions.md` |110| Prefer explicit context (params) over implicit `this` | `rules/prefer-explicit-context-over-this.md` |111| Do not use barrel files (`index.js`/`index.ts` re-exports) | `rules/no-barrel-files.md` |112| Avoid intermediate arrays on hot paths (`filter().map()` chains) | `rules/avoid-intermediate-arrays.md` |113| `undefined` for absence, `null` for API/external contracts | `rules/null-undefined.md` |114115### TypeScript-specific116117| Rule | File |118| ------------------------------------------------------------------ | --------------------------------------------- |119| Avoid type assertions (`as T`, `!`, `as unknown as T`) | `rules/avoid-type-assertions.md` |120| Favor existing types over `as const` | `rules/favor-existing-types-over-as-const.md` |121| Do not prefix interfaces with `I` | `rules/no-interface-prefix.md` |122| Mark properties and arrays `readonly` to signal immutability | `rules/readonly.md` |123| Annotate function return types explicitly; enable `noImplicitAny` | `rules/explicit-return-types.md` |124| Use modules instead of namespaces; prefer named exports | `rules/module-organization.md` |125| Prefer shipped types / `@types/*`; otherwise add a minimal `.d.ts` | `rules/js-interop-declarations.md` |126127---128129## Benchmark130131This router has no scenario of its own. Gate data lives in the leaf footers:132133- `type-system/SKILL.md` → `## Benchmark` (scenarios `typescript-001` PASS and `typescript-002` SOFT PASS, run 2026-08-31).134- `composition/SKILL.md` → `## Benchmark` (scenario `composing-software-001`, run 2026-08-31, PASS).135- Historical optimizer runs: `run-history.md` (includes per-skill gate targets).136137Gate per `.agents/skills/skill-optimizer/rules/release-gates.md`.