skills/jwynia/agent-skills/typescript-best-practices

# typescript-best-practices

SKILL.md

# TypeScript Best Practices

Guide AI agents in writing high-quality TypeScript code. This skill provides coding standards, architecture patterns, and tools for analysis and scaffolding.

## When to Use This Skill

Use this skill when:

- Generating new TypeScript code
- Reviewing TypeScript files for quality issues
- Creating new modules, services, or components
- Refactoring JavaScript to TypeScript
- Answering questions about TypeScript patterns or types
- Designing APIs or interfaces

Do NOT use this skill when:

- Working with pure JavaScript (no TypeScript)
- Debugging runtime errors (use debugging tools)
- Framework-specific patterns (React, Vue, etc. - use framework skills)

## Core Principles

### 1. Type Safety First

Maximize compile-time error detection:

    // Prefer unknown over any for unknown types
    function processInput(data: unknown): string {
      if (typeof data === "string") return data;
      if (typeof data === "number") return String(data);
      throw new Error("Unsupported type");
    }

    // Explicit return types for public APIs
    export function calculateTotal(items: ReadonlyArray<Item>): number {
      return items.reduce((sum, item) => sum + item.price, 0);
    }

    // Use const assertions for literal types
    const CONFIG = {
      mode: "production",
      version: 1,
    } as const;

### 2. Immutability by Default

Prevent accidental mutations:

    // Use readonly for object properties
    interface User {
      readonly id: string;
      readonly email: string;
      name: string; // Only mutable if intentional
    }

    // Use ReadonlyArray for collections
    function processItems(items: ReadonlyArray<Item>): ReadonlyArray<Result> {
      return items.map(transform);
    }

    // Prefer spreading over mutation
    function updateUser(user: User, name: string): User {
      return { ...user, name };
    }

### 3. Error Handling with Types

Use the type system for error handling:

    // Result type for recoverable errors
    type Result<T, E = Error> =
      | { success: true; value: T }
      | { success: false; error: E };

    // Typed error classes
    class ValidationError extends Error {
      constructor(
        message: string,
        readonly field: string,
        readonly code: string
      ) {
        super(message);
        this.name = "ValidationError";
      }
    }

    // Function with Result return type
    function parseConfig(input: string): Result<Config, ValidationError> {
      try {
        const data = JSON.parse(input);
        if (!isValidConfig(data)) {
          return {
            success: false,
            error: new ValidationError("Invalid config", "root", "INVALID_FORMAT"),
          };
        }
        return { success: true, value: data };
      } catch {
        return {
          success: false,
          error: new ValidationError("Parse failed", "root", "PARSE_ERROR"),
        };
      }
    }

### 4. Code Organization

Structure code for maintainability:

    // One concept per file
    // user.ts - User type and related utilities
    export interface User {
      readonly id: string;
      readonly email: string;
      readonly createdAt: Date;
    }

    export function createUser(email: string): User {
      return {
        id: crypto.randomUUID(),
        email,
        createdAt: new Date(),
      };
    }

    // Explicit exports (no barrel file wildcards)
    // index.ts
    export { User, createUser } from "./user.ts";
    export { validateEmail } from "./validation.ts";

## Quick Reference

Category | Prefer | Avoid
--- | --- | ---
Unknown types | `unknown` | `any`
Collections | `ReadonlyArray<T>` | `T[]` for inputs
Objects | `Readonly<T>` | Mutable by default
Null checks | Optional chaining `?.` | `!= null`
Type narrowing | Type guards | `as` assertions
Return types | Explicit on exports | Inferred on exports
Enums | String literal unions | Numeric enums
Imports | Named imports | Default imports
Errors | Result types | Throwing for flow control
Loops | `for...of`, `.map()` | `for...in` on arrays

## Code Generation Guidelines

When generating TypeScript code, follow these patterns:

### Module Structure

    /**
     * Module description
     * @module module-name
     */

    // === Types ===
    export interface ModuleOptions {
      readonly setting: string;
    }

    export interface ModuleResult {
      readonly data: unknown;
    }

    // === Constants ===
    const DEFAULT_OPTIONS: ModuleOptions = {
      setting: "default",
    };

    // === Implementation ===
    export function processData(
      input: unknown,
      options: Partial<ModuleOptions> = {}
    ): ModuleResult {
      const opts = { ...DEFAULT_OPTIONS, ...options };
      // Implementation
      return { data: input };
    }

### Function Design

    // Pure functions preferred
    function transform(input: Input): Output {
      // No side effects, same input = same output
      return { ...input, processed: true };
    }

    // Explicit parameter types
    function fetchUser(id: string, options?: FetchOptions): Promise<User> {
      // Implementation
    }

    // Use function overloads for complex signatures
    function parse(input: string): ParsedData;
    function parse(input: Buffer): ParsedData;
    function parse(input: string | Buffer): ParsedData {
      // Implementation
    }

### Interface Design

    // Prefer interfaces for object shapes
    interface UserData {
      readonly id: string;
      readonly email: string;
    }

    // Use type for unions and intersections
    type UserRole = "admin" | "user" | "guest";
    type AdminUser = UserData & { readonly role: "admin" };

    // Document with JSDoc
    /**
     * Configuration for the API client
     * @property baseUrl - The base URL for API requests
     * @property timeout - Request timeout in milliseconds
     */
    interface ApiConfig {
      readonly baseUrl: string;
      readonly timeout?: number;
    }

## Common Anti-Patterns

Avoid these patterns when generating code:

Anti-Pattern | Problem | Solution
--- | --- | ---
`any` type | Disables type checking | Use `unknown` and narrow
`as` assertions | Runtime errors | Use type guards
Non-null `!` | Null pointer errors | Optional chaining `?.`
Mutable params | Unexpected mutations | `Readonly<T>`
Magic strings | Typos, no autocomplete | String literal types
God classes | Hard to test/maintain | Single responsibility
Circular deps | Build/runtime issues | Dependency inversion
Index signatures | Lose type info | Explicit properties

See `references/anti-patterns/common-mistakes.md` for detailed examples.

## Scripts Reference

### analyze.ts

Analyze TypeScript code for quality issues:

    deno run --allow-read scripts/analyze.ts <path> [options]

    Options:
      --strict        Enable all checks
      --json          Output JSON for programmatic use
      --fix-hints     Show suggested fixes

    Examples:
      # Analyze a file
      deno run --allow-read scripts/analyze.ts ./src/utils.ts

      # Analyze directory with strict mode
      deno run --allow-read scripts/analyze.ts ./src --strict

      # JSON output for CI
      deno run --allow-read scripts/analyze.ts ./src --json

### generate-types.ts

Generate TypeScript types from JSON data:

    deno run --allow-read --allow-write scripts/generate-types.ts <input> [options]

    Options:
      --name <name>   Root type name (default: inferred)
      --output <path> Output file path
      --readonly      Generate readonly types
      --interface     Use interface instead of type

    Examples:
      # Generate from JSON file
      deno run --allow-read scripts/generate-types.ts ./data.json --name Config

      # Generate readonly interface
      deno run --allow-read --allow-write scripts/generate-types.ts ./api-response.json \
        --interface --readonly --output ./types/api.ts

### scaffold-module.ts

Create properly structured TypeScript modules:

    deno run --allow-read --allow-write scripts/scaffold-module.ts [options]

    Options:
      --name <name>   Module name (required)
      --path <path>   Target directory (default: ./src)
      --type <type>   Type: service, util, component
      --with-tests    Include test file

    Examples:
      # Create a utility module
      deno run --allow-read --allow-write scripts/scaffold-module.ts \
        --name "string-utils" --type util

      # Create a service with tests
      deno run --allow-read --allow-write scripts/scaffold-module.ts \
        --name "user-service" --type service --with-tests

## Additional Resources

### Type System Deep Dives

- `references/type-system/advanced-types.md` - Generics, conditional types, mapped types
- `references/type-system/type-guards.md` - Type narrowing techniques
- `references/type-system/utility-types.md` - Built-in utility types

### Pattern Guides

- `references/patterns/error-handling.md` - Result types, typed errors
- `references/patterns/async-patterns.md` - Async/await best practices
- `references/patterns/functional-patterns.md` - Immutability, composition
- `references/patterns/module-patterns.md` - Exports, dependency injection

### Architecture

- `references/architecture/project-structure.md` - Directory organization
- `references/architecture/api-design.md` - Interface design, versioning

### Templates

- `assets/templates/module-template.ts.md` - Module starter template
- `assets/templates/service-template.ts.md` - Service class template
- `assets/tsconfig-presets/strict.json` - Maximum strictness config
- `assets/tsconfig-presets/recommended.json` - Balanced defaults

Weekly Installs
67
Repository
jwynia/agent-skills
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust Hub Pass
Socket Pass
Snyk Pass
Installed on
opencode 49
claude-code 48
gemini-cli 46
codex 43
github-copilot 40
antigravity 32