Agent Skills: TypeScript Core Patterns

Advanced TypeScript patterns and best practices for 2025

UncategorizedID: bobmatnyc/claude-mpm-skills/typescript-core

Install this agent skill to your local

pnpm dlx add-skill https://github.com/bobmatnyc/claude-mpm-skills/tree/HEAD/toolchains/typescript/typescript-core

Skill Files

Browse the full folder contents for typescript-core.

Download Skill

Loading file tree…

toolchains/typescript/typescript-core/SKILL.md

Skill Metadata

Name
typescript-core
Description
"Advanced TypeScript patterns and best practices for 2025"

TypeScript Core Patterns

Modern TypeScript development patterns for type safety, runtime validation, and optimal configuration.

Quick Start

New Project: Use 2025 tsconfig → Enable strict + noUncheckedIndexedAccess → Choose Zod for validation

Existing Project: Enable strict: false initially → Fix any with unknown → Add noUncheckedIndexedAccess

API Development: Zod schemas at boundaries → z.infer<typeof Schema> for types → satisfies for routes

Library Development: Enable declaration: true → Use const type parameters → See advanced-patterns-2025.md

Quick Reference

tsconfig.json 2025 Baseline

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "skipLibCheck": true,
    "declaration": true,
    "declarationMap": true
  }
}

Key Compiler Options

| Option | Purpose | When to Enable | |--------|---------|----------------| | noUncheckedIndexedAccess | Forces null checks on array/object access | Always for safety | | exactOptionalPropertyTypes | Distinguishes undefined from missing | APIs with optional fields | | verbatimModuleSyntax | Enforces explicit type-only imports | ESM projects | | erasableSyntaxOnly | Node.js 22+ native TS support | Type stripping environments |

Local Baselines

See references/configuration.md for repo-specific tsconfig patterns (CommonJS CLI, NodeNext strict, Next.js bundler).

Core Type Patterns

Const Type Parameters

Preserve literal types through generic functions:

function createConfig<const T extends Record<string, unknown>>(config: T): T {
  return config;
}

const config = createConfig({ 
  apiUrl: "https://api.example.com", 
  timeout: 5000 
});
// Type: { readonly apiUrl: "https://api.example.com"; readonly timeout: 5000 }

Satisfies Operator

Validate against a type while preserving literal inference:

type Route = { path: string; children?: Routes };
type Routes = Record<string, Route>;

const routes = {
  AUTH: { path: "/auth" },
  HOME: { path: "/" }
} satisfies Routes;

routes.AUTH.path;     // Type: "/auth" (literal preserved)
routes.NONEXISTENT;   // ❌ Type error

Template Literal Types

Type-safe string manipulation and route extraction:

type ExtractParams<T extends string> = 
  T extends `${string}:${infer Param}/${infer Rest}`
    ? Param | ExtractParams<Rest>
    : T extends `${string}:${infer Param}`
      ? Param
      : never;

type Params = ExtractParams<"/users/:id/posts/:postId">; // "id" | "postId"

Discriminated Unions with Exhaustiveness

type Result<T, E = Error> = 
  | { success: true; data: T }
  | { success: false; error: E };

function handleResult<T>(result: Result<T>): T {
  if (result.success) return result.data;
  throw result.error;
}

// Exhaustiveness checking
type Action = 
  | { type: 'create'; payload: string }
  | { type: 'delete'; id: number };

function handle(action: Action) {
  switch (action.type) {
    case 'create': return action.payload;
    case 'delete': return action.id;
    default: {
      const _exhaustive: never = action;
      throw new Error(`Unhandled: ${_exhaustive}`);
    }
  }
}

Runtime Validation

TypeScript types disappear at runtime. Use validation libraries for external data (APIs, forms, config files).

Quick Comparison

| Library | Bundle Size | Speed | Best For | |---------|-------------|-------|----------| | Zod | ~13.5kB | Baseline | Full-stack apps, tRPC integration | | TypeBox | ~8kB | ~10x faster | OpenAPI, performance-critical | | Valibot | ~1.4kB | ~2x faster | Edge functions, minimal bundles |

Basic Pattern (Zod)

import { z } from "zod";

const UserSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  role: z.enum(["admin", "user", "guest"]),
});

type User = z.infer<typeof UserSchema>;

// Validate external data
function parseUser(input: unknown): User {
  return UserSchema.parse(input);
}

→ See runtime-validation.md for complete Zod, TypeBox, and Valibot patterns

Decision Support

Quick Decision Guide

Need to choose between type vs interface?

  • Public API / library types → interface
  • Union types / mapped types → type
  • Simple object shapes → interface (default)

Need generics or union types?

  • Output type depends on input type → Generics
  • Fixed set of known types → Union types
  • Building reusable data structures → Generics

Dealing with unknown data?

  • External data (API, user input) → unknown (type-safe)
  • Rapid prototyping / migration → any (temporarily)

Need runtime validation?

  • Full-stack TypeScript with tRPC → Zod
  • OpenAPI / high performance → TypeBox
  • Edge functions / minimal bundle → Valibot

→ See decision-trees.md for comprehensive decision frameworks

Troubleshooting

Common Issues Quick Reference

Property does not exist on type → Define proper interface or use optional properties

Type is not assignable → Fix property types or use runtime validation (Zod)

Object is possibly 'undefined' → Use optional chaining (?.) or type guards

Cannot find module → Check file extensions (.js for ESM) and module resolution

Slow compilation → Enable incremental, use skipLibCheck, consider esbuild/swc

→ See troubleshooting.md for detailed solutions with examples

Navigation

Detailed References

  • 📐 Advanced Types - Conditional types, mapped types, infer keyword, recursive types. Load when building complex type utilities or generic libraries.

  • ⚙️ Configuration - Complete tsconfig.json guide, project references, monorepo patterns. Load when setting up new projects or optimizing builds.

  • 🔒 Runtime Validation - Zod, TypeBox, Valibot deep patterns, error handling, integration strategies. Load when implementing API validation or form handling.

  • ✨ Advanced Patterns 2025 - TypeScript 5.2+ features: using keyword, stable decorators, import type behavior, satisfies with generics. Load when using modern language features.

  • 🌳 Decision Trees - Clear decision frameworks for type vs interface, generics vs unions, unknown vs any, validation library selection, type narrowing strategies, and module resolution. Load when making TypeScript design decisions.

  • 🔧 Troubleshooting - Common TypeScript errors and fixes, type inference issues, module resolution problems, tsconfig misconfigurations, build performance optimization, and type compatibility errors. Load when debugging TypeScript issues.

JavaScript / Runtime Quality Anti-Patterns

The type system catches type errors, but a class of JavaScript defects is runtime/AST- level and survives into emitted JS and plain-JS files. Watch for:

  • Loose equality (==/!=) — coercion bugs and auth flaws; use ===/!== (accept == null only when commented as "null or undefined").
  • Dynamic code execution (eval, new Function(str), string setTimeout) — injection risk and unnecessary; use direct syntax.
  • Mutating builtins (Object/Array/Function.prototype) — pollutes for…in, breaks the whole runtime; extend or use free functions instead.
  • Variable shadowing, var instead of let/const, using functions before declaration — readability and "wrong variable" bugs.
  • Logical OR in switch case labels (case 1 || 2: only matches 1) — use stacked case labels.
  • Repetitive deep-member access — cache the resolved chain in a local (esp. DOM).
  • Non-wrapped IIFEs, backslash multiline strings, new Array() — readability and well-known traps; prefer wrapped IIFEs, template literals, and array literals.

See JS/TS Quality Anti-Patterns for each defect with compliant/non-compliant examples, severities, false-positive filters, and the equivalent ESLint/SonarSource rule. Derived from CAST Highlight JavaScript code quality indicators (https://doc.casthighlight.com/), cross-referenced to ESLint core rules and SonarSource RSPEC.

Red Flags

Stop and reconsider if:

  • Using any instead of unknown for external data
  • Casting with as without runtime validation
  • Disabling strict mode for convenience
  • Using @ts-ignore without clear justification
  • Index access without noUncheckedIndexedAccess

Integration with Other Skills

  • nextjs-core: Type-safe Server Actions and route handlers
  • nextjs-v16: Async API patterns and Cache Components typing
  • mcp-builder: Zod schemas for MCP tool inputs

Related Skills

When using Core, these skills enhance your workflow:

  • react: TypeScript with React: component typing, hooks, generics
  • nextjs: TypeScript in Next.js: Server Components, Server Actions typing
  • drizzle: Type-safe database queries with Drizzle ORM
  • prisma: Prisma's generated TypeScript types for database schemas

[Full documentation available in these skills if deployed in your bundle]