Files
2025-11-29 18:14:56 +08:00

5.6 KiB

name, description
name description
lang-typescript Write clean, type-safe TypeScript code using modern patterns, strict configuration, and best practices. Use when writing TypeScript code, configuring projects, or solving type-related challenges.

TypeScript Expert

Write clean, type-safe TypeScript code that leverages the full power of the type system to catch bugs at compile time.

When to Use This Skill

Use this skill when:

  • Writing or refactoring TypeScript code
  • Configuring TypeScript projects (tsconfig.json)
  • Solving complex type-related challenges
  • Choosing between type system patterns
  • Validating external data with types

Core Workflow

1. Type Decision Tree

Choose the right construct:

Use Case Use Not
Object shapes interface type
Unions/primitives type interface
Dynamic data unknown any
State machines Discriminated unions Complex conditionals
Domain types Branded types Plain primitives

Example:

// ✅ Correct choices
interface User {
  id: number;
  name: string;
} // Object shape
type Status = "idle" | "loading" | "success"; // Union
type USD = number & { readonly __brand: "USD" }; // Branded type

// ❌ Wrong choices
type User = { id: number }; // Use interface
interface Status {
  /* ... */
} // Can't do unions

2. State Modeling Pattern

For finite states, always use discriminated unions to eliminate impossible states:

type ApiState =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: string }
  | { status: "error"; message: string };

// Exhaustiveness checking
function handle(state: ApiState) {
  switch (state.status) {
    case "success":
      return state.data;
    case "error":
      return state.message;
    case "idle":
      return "Not started";
    case "loading":
      return "Loading...";
    default:
      const _exhaustive: never = state; // Compiler error if cases missing
      throw new Error("Unhandled state");
  }
}

3. Validation Workflow

Always validate external data with runtime checks:

  1. Simple validation: Type guards
  2. Complex validation: Schema libraries (Zod, Yup)
// Type guard pattern
function isUser(data: unknown): data is User {
  return (
    typeof data === "object" &&
    data !== null &&
    "id" in data &&
    typeof (data as any).id === "number"
  );
}

// Schema validation (preferred for APIs)
import { z } from "zod";

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
});

type User = z.infer<typeof UserSchema>;
const user = UserSchema.parse(apiData); // Runtime validation + types

Never use type assertions without validation:

// ❌ BAD: No runtime check
const user = data as User;

// ✅ GOOD: Validated first
if (isUser(data)) {
  // data is User here
}

4. Configuration Checklist

For new projects, enable strict mode plus:

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noPropertyAccessFromIndexSignature": true,
    "moduleResolution": "bundler"
  }
}

Why these matter:

  • noUncheckedIndexedAccess - Prevents undefined access bugs
  • exactOptionalPropertyTypes - Distinguishes missing from undefined
  • moduleResolution: "bundler" - Optimized for Vite/esbuild

5. Code Organization Rules

Barrel files: Avoid for internal code (75% faster builds)

// ❌ BAD: Internal barrel
// src/components/index.ts
export * from "./Button";

// ✅ GOOD: Direct imports + path aliases
// tsconfig.json: "paths": { "@/components/*": ["src/components/*"] }
import { Button } from "@/components/Button";

Only use barrel files for:

  • Library entry points
  • Public APIs
  • Small modules (<10 exports)

Quick Reference Patterns

Utility Types

type UserPreview = Pick<User, "id" | "name">; // Extract subset
type PublicUser = Omit<User, "email">; // Remove fields
type UpdateDto = Partial<User>; // Make optional
type CompleteUser = Required<User>; // Make required
type ImmutableUser = Readonly<User>; // Make readonly
type UserType = ReturnType<typeof getUser>; // Extract return type

Error Handling

// Catch with unknown
try {
} catch (err: unknown) {
  if (err instanceof Error) {
    /* ... */
  }
}

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

Readonly Patterns

const config: Readonly<Config> = {
  /* ... */
};
const numbers: readonly number[] = [1, 2, 3];
const ROUTES = { home: "/" } as const; // Deep readonly + literal types

When to Consult Detailed References

For comprehensive information on advanced patterns, configuration options, or specific features, read:

  • references/best-practices-2025.md - Full TypeScript best practices guide

The reference includes:

  • Advanced type patterns (conditional types, mapped types, branded types)
  • Complete tsconfig.json options
  • Modern features (decorators, const type parameters)
  • Common anti-patterns and solutions

Quality Checklist

Before completing TypeScript code:

  • External data validated (not just typed)
  • No any types (or explicitly justified)
  • State machines use discriminated unions
  • Utility types used where applicable
  • Readonly applied to prevent mutations
  • Exhaustiveness checks with never