Typescript Pro

TypeScript Pro automation, integration, and advanced type-safe development workflows

TypeScript Pro is an AI skill that provides advanced guidance for leveraging TypeScript's type system to build safer, more maintainable applications. It covers advanced type patterns, generics, conditional types, template literal types, compiler configuration, and migration strategies that maximize the value of TypeScript's static analysis.

What Is This?

Overview

TypeScript Pro delivers expert-level type system techniques beyond basic type annotations. It addresses advanced generic patterns with constraints and inference, conditional types and mapped types for transforming type shapes, discriminated unions for exhaustive pattern matching, template literal types, compiler configuration tuning, and incremental migration strategies for JavaScript projects.

Who Should Use This

This skill serves TypeScript developers seeking to use the type system more effectively, library authors designing type-safe public APIs, teams adopting TypeScript and needing guidance on configuration and patterns, and senior engineers reviewing TypeScript code for type safety and correctness.

Why Use It?

Problems It Solves

Basic TypeScript usage with simple type annotations captures only a fraction of the type system's power. Teams that use any to bypass type errors lose the safety benefits of TypeScript. Complex data transformations produce types that are difficult to express without advanced patterns.

Core Highlights

The skill demonstrates how to express complex data relationships in the type system so the compiler catches errors. Generic utilities reduce code duplication while preserving type safety. Discriminated unions enable exhaustive checking that prevents missing case handling. Strict compiler configuration catches more bugs at compile time.

How to Use It?

Basic Usage

// Discriminated unions with exhaustive handling
type ApiResponse<T> =
  | { status: 'success'; data: T }
  | { status: 'error'; error: string; code: number }
  | { status: 'loading' };

function handleResponse<T>(response: ApiResponse<T>): string {
  switch (response.status) {
    case 'success':
      return `Data received: ${JSON.stringify(response.data)}`;
    case 'error':
      return `Error ${response.code}: ${response.error}`;
    case 'loading':
      return 'Loading...';
    default:
      const _exhaustive: never = response;
      return _exhaustive;
  }
}

// Generic constraint with inference
function groupBy<T, K extends keyof T>(
  items: T[],
  key: K
): Map<T[K], T[]> {
  const map = new Map<T[K], T[]>();
  for (const item of items) {
    const groupKey = item[key];
    const group = map.get(groupKey) ?? [];
    group.push(item);
    map.set(groupKey, group);
  }
  return map;
}

Real-World Examples

// Type-safe event emitter using mapped types
type EventMap = {
  'user:login': { userId: string; timestamp: number };
  'user:logout': { userId: string };
  'order:created': { orderId: string; total: number };
};

class TypedEventEmitter<Events extends Record<string, unknown>> {
  private listeners = new Map<string, Set<Function>>();

  on<E extends keyof Events>(
    event: E,
    handler: (payload: Events[E]) => void
  ): () => void {
    const key = event as string;
    if (!this.listeners.has(key)) this.listeners.set(key, new Set());
    this.listeners.get(key)!.add(handler);
    return () => this.listeners.get(key)?.delete(handler);
  }

  emit<E extends keyof Events>(event: E, payload: Events[E]): void {
    const key = event as string;
    this.listeners.get(key)?.forEach(fn => fn(payload));
  }
}

const emitter = new TypedEventEmitter<EventMap>();
emitter.on('user:login', (data) => {
  console.log(data.userId); // fully typed
});

Advanced Tips

Use the satisfies operator to validate that a value matches a type while preserving its narrower literal type. Create branded types using intersection with unique symbols to prevent mixing semantically different values that share the same primitive type. Enable noUncheckedIndexedAccess in tsconfig to catch potential undefined access on arrays and records.

When to Use It?

Use Cases

Use TypeScript Pro when designing library APIs that need to provide excellent type inference for consumers, when building complex data processing pipelines where type safety prevents runtime errors, when migrating a JavaScript codebase to TypeScript incrementally, or when reviewing TypeScript code for type safety improvements.

Related Topics

TypeScript compiler API, ESLint with typescript-eslint for linting, Zod for runtime validation with type inference, declaration file authoring for libraries, and TypeScript performance optimization all complement advanced TypeScript development.

Important Notes

Requirements

TypeScript 5.x or later for access to the latest type system features. A tsconfig.json with strict mode enabled for maximum type checking. An editor with TypeScript language server support for real-time type feedback.

Usage Recommendations

Do: enable strict mode and address all type errors rather than suppressing them. Use discriminated unions instead of optional fields when a value's shape depends on a status field. Define explicit return types on public API functions to prevent accidental type changes.

Don't: use any to silence type errors, as unknown with type narrowing is always safer. Create overly complex utility types that teammates cannot understand or maintain. Rely on type assertions (as) when proper type narrowing with guards would work.

Limitations

TypeScript's type system operates at compile time only and cannot prevent runtime type violations from external data. Complex generic types can significantly slow down the TypeScript compiler. Some valid JavaScript patterns are difficult or impossible to express in TypeScript's type system.