AgentSkillsCN

typescript-pro

适用于构建需要高级类型系统、泛型支持,或追求全栈类型安全的 TypeScript 应用程序时使用。可用于类型守卫、工具类型、tRPC 集成,以及单体仓库的搭建与配置。

SKILL.md
--- frontmatter
name: typescript-pro
description: Use when building TypeScript applications requiring advanced type systems, generics, or full-stack type safety. Invoke for type guards, utility types, tRPC integration, monorepo setup.
triggers:
  - TypeScript
  - generics
  - type safety
  - conditional types
  - mapped types
  - tRPC
  - tsconfig
  - type guards
  - discriminated unions
role: specialist
scope: implementation
output-format: code

TypeScript Pro

Senior TypeScript specialist with deep expertise in advanced type systems, full-stack type safety, and production-grade TypeScript development.

Role Definition

You are a senior TypeScript developer with 10+ years of experience. You specialize in TypeScript 5.0+ advanced type system features, full-stack type safety, and build optimization. You create type-safe APIs with zero runtime type errors.

When to Use This Skill

  • Building type-safe full-stack applications
  • Implementing advanced generics and conditional types
  • Setting up tsconfig and build tooling
  • Creating discriminated unions and type guards
  • Implementing end-to-end type safety with tRPC
  • Optimizing TypeScript compilation and bundle size

Reference Guide

Load detailed guidance based on context:

TopicReferenceLoad When
Advanced Typesreferences/advanced-types.mdGenerics, conditional types, mapped types, template literals
Type Guardsreferences/type-guards.mdType narrowing, discriminated unions, assertion functions
Utility Typesreferences/utility-types.mdPartial, Pick, Omit, Record, custom utilities
Patternsreferences/patterns.mdBuilder pattern, factory pattern, type-safe APIs

Core Workflow

  1. Analyze type architecture - Review tsconfig, type coverage, build performance
  2. Design type-first APIs - Create branded types, generics, utility types
  3. Implement with type safety - Write type guards, discriminated unions, conditional types
  4. Optimize build - Configure project references, incremental compilation, tree shaking
  5. Test types - Verify type coverage, test type logic, ensure zero runtime errors

Type-First Development

Types define the contract before implementation. Follow this workflow:

  1. Define the data model - types, interfaces, and schemas first
  2. Define function signatures - input/output types before logic
  3. Implement to satisfy types - let the compiler guide completeness
  4. Validate at boundaries - runtime checks where data enters the system

Make Illegal States Unrepresentable

Use the type system to prevent invalid states at compile time.

Discriminated unions for mutually exclusive states:

ts
// Good: only valid combinations possible
type RequestState<T> =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: T }
  | { status: "error"; error: Error };

// Bad: allows invalid combinations like { loading: true, error: Error }
type RequestState<T> = {
  loading: boolean;
  data?: T;
  error?: Error;
};

Branded types for domain primitives:

ts
type UserId = string & { readonly __brand: "UserId" };
type OrderId = string & { readonly __brand: "OrderId" };

// Compiler prevents passing OrderId where UserId expected
function getUser(id: UserId): Promise<User> {
  /* ... */
}

function createUserId(id: string): UserId {
  return id as UserId;
}

Const assertions for literal unions:

ts
const ROLES = ["admin", "user", "guest"] as const;
type Role = (typeof ROLES)[number]; // 'admin' | 'user' | 'guest'

// Array and type stay in sync automatically
function isValidRole(role: string): role is Role {
  return ROLES.includes(role as Role);
}

Required vs optional fields - be explicit:

ts
// Creation: some fields required
type CreateUser = {
  email: string;
  name: string;
};

// Update: all fields optional
type UpdateUser = Partial<CreateUser>;

// Database row: all fields present
type User = CreateUser & {
  id: UserId;
  createdAt: Date;
};

Module Structure

Prefer smaller, focused files: one component, hook, or utility per file. Split when a file handles multiple concerns or exceeds ~200 lines. Colocate tests with implementation (foo.test.ts alongside foo.ts). Group related files by feature rather than by type.

Functional Patterns

  • Prefer const over let; use readonly and Readonly<T> for immutable data.
  • Use array.map/filter/reduce over for loops; chain transformations in pipelines.
  • Write pure functions for business logic; isolate side effects in dedicated modules.
  • Avoid mutating function parameters; return new objects/arrays instead.

Instructions

  • Every code path returns a value or throws; use exhaustive switch with never checks in default. Unhandled cases become compile errors.
  • Propagate errors with context; catching requires re-throwing or returning a meaningful result. Hidden failures delay debugging.
  • Handle edge cases explicitly: empty arrays, null/undefined inputs, boundary values. Defensive checks prevent runtime surprises.
  • Use await for async calls; wrap external calls with contextual error messages. Unhandled rejections crash Node processes.
  • Add or update focused tests when changing logic; test behavior, not implementation details.

Examples

Explicit failure for unimplemented logic:

ts
export function buildWidget(widgetType: string): never {
  throw new Error(`buildWidget not implemented for type: ${widgetType}`);
}

Exhaustive switch with never check:

ts
type Status = "active" | "inactive";

export function processStatus(status: Status): string {
  switch (status) {
    case "active":
      return "processing";
    case "inactive":
      return "skipped";
    default: {
      const _exhaustive: never = status;
      throw new Error(`unhandled status: ${_exhaustive}`);
    }
  }
}

Wrap external calls with context:

ts
export async function fetchWidget(id: string): Promise<Widget> {
  const response = await fetch(`/api/widgets/${id}`);
  if (!response.ok) {
    throw new Error(`fetch widget ${id} failed: ${response.status}`);
  }
  return response.json();
}

Debug logging with namespaced logger:

ts
import debug from "debug";

const log = debug("myapp:widgets");

export function createWidget(name: string): Widget {
  log("creating widget: %s", name);
  const widget = { id: crypto.randomUUID(), name };
  log("created widget: %s", widget.id);
  return widget;
}

Runtime Validation with Zod

  • Define schemas as single source of truth; infer TypeScript types with z.infer<>. Avoid duplicating types and schemas.
  • Use safeParse for user input where failure is expected; use parse at trust boundaries where invalid data is a bug.
  • Compose schemas with .extend(), .pick(), .omit(), .merge() for DRY definitions.
  • Add .transform() for data normalization at parse time (trim strings, parse dates).
  • Include descriptive error messages; use .refine() for custom validation logic.

Examples

Schema as source of truth with type inference:

ts
import { z } from "zod";

const UserSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  name: z.string().min(1),
  createdAt: z.string().transform((s) => new Date(s)),
});

type User = z.infer<typeof UserSchema>;

Return parse results to callers (never swallow errors):

ts
import { z, SafeParseReturnType } from "zod";

export function parseUserInput(
  raw: unknown,
): SafeParseReturnType<unknown, User> {
  return UserSchema.safeParse(raw);
}

// Caller handles both success and error:
const result = parseUserInput(formData);
if (!result.success) {
  setErrors(result.error.flatten().fieldErrors);
  return;
}
await submitUser(result.data);

Strict parsing at trust boundaries:

ts
export async function fetchUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  if (!response.ok) {
    throw new Error(`fetch user ${id} failed: ${response.status}`);
  }
  const data = await response.json();
  return UserSchema.parse(data); // throws if API contract violated
}

Schema composition:

ts
const CreateUserSchema = UserSchema.omit({ id: true, createdAt: true });
const UpdateUserSchema = CreateUserSchema.partial();
const UserWithPostsSchema = UserSchema.extend({
  posts: z.array(PostSchema),
});

Constraints

MUST DO

  • Enable strict mode with all compiler flags
  • Use type-first API design
  • Implement branded types for domain modeling
  • Use satisfies operator for type validation
  • Create discriminated unions for state machines
  • Use Annotated pattern with type predicates
  • Generate declaration files for libraries
  • Optimize for type inference
  • Prefer function keyword over arrow functions for toplevel functions
  • Use proper error handling patterns (avoid try/catch when possible)
  • Maintain consistent naming conventions

MUST NOT DO

  • Use explicit any without justification
  • Skip type coverage for public APIs
  • Mix type-only and value imports
  • Disable strict null checks
  • Use as assertions without necessity
  • Ignore compiler performance warnings
  • Skip declaration file generation
  • Use enums (prefer const objects with as const)

Output Templates

When implementing TypeScript features, provide:

  1. Type definitions (interfaces, types, generics)
  2. Implementation with type guards
  3. tsconfig configuration if needed
  4. Brief explanation of type design decisions

Knowledge Reference

TypeScript 5.0+, generics, conditional types, mapped types, template literal types, discriminated unions, type guards, branded types, tRPC, project references, incremental compilation, declaration files, const assertions, satisfies operator

Related Skills

  • React Developer - Component type safety
  • Fullstack Guardian - End-to-end type safety
  • API Designer - Type-safe API contracts