AgentSkillsCN

Typescript Expert

TypeScript 专家

SKILL.md

TypeScript Expert Skill

Master TypeScript's type system for safer, more maintainable code

🎯 PRINCIPLES

1. Type Safety First

  • Prefer strict mode (strict: true)
  • Avoid any - use unknown for truly unknown types
  • Enable all strict checks in tsconfig.json

2. Interface vs Type

typescript
// ✅ Use interface for objects that will be extended
interface User {
  id: string;
  name: string;
}

interface AdminUser extends User {
  permissions: string[];
}

// ✅ Use type for unions, intersections, primitives
type Status = 'pending' | 'active' | 'inactive';
type ID = string | number;
type Callback<T> = (value: T) => void;

3. Generics Best Practices

  • Name meaningfully: TItem, TKey, TValue, not just T
  • Add constraints when possible: <T extends object>
  • Default generics for common cases: <T = unknown>

📘 UTILITY TYPES

Built-in Utility Types

typescript
// Pick - Select specific properties
type UserPreview = Pick<User, 'id' | 'name'>;

// Omit - Exclude properties
type CreateUserInput = Omit<User, 'id' | 'createdAt'>;

// Partial - Make all properties optional
type UpdateUserInput = Partial<User>;

// Required - Make all properties required
type CompleteUser = Required<User>;

// Readonly - Make all properties readonly
type ImmutableUser = Readonly<User>;

// Record - Create object type with specific keys
type UserRoles = Record<string, string[]>;

// ReturnType - Extract function return type
type GetUserResult = ReturnType<typeof getUser>;

// Parameters - Extract function parameters
type GetUserParams = Parameters<typeof getUser>;

// Awaited - Unwrap Promise type
type ResolvedUser = Awaited<Promise<User>>;

Custom Utility Types

typescript
// DeepPartial - Recursive partial
type DeepPartial<T> = T extends object ? {
  [P in keyof T]?: DeepPartial<T[P]>;
} : T;

// NonNullable properties
type NonNullableFields<T> = {
  [P in keyof T]: NonNullable<T[P]>;
};

// Extract function properties
type FunctionProperties<T> = {
  [K in keyof T as T[K] extends Function ? K : never]: T[K];
};

🔄 DISCRIMINATED UNIONS

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

function processResult<T>(result: Result<T>) {
  if (result.success) {
    // TypeScript knows: result.data exists
    return result.data;
  } else {
    // TypeScript knows: result.error exists
    throw result.error;
  }
}

// ✅ State Machine Pattern
type RequestState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error };

function renderState<T>(state: RequestState<T>) {
  switch (state.status) {
    case 'idle':
      return 'Ready to load';
    case 'loading':
      return 'Loading...';
    case 'success':
      return `Data: ${state.data}`;
    case 'error':
      return `Error: ${state.error.message}`;
  }
}

🛡️ TYPE GUARDS

typescript
// ✅ Type Predicate Functions
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function isUser(value: unknown): value is User {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    'name' in value
  );
}

// ✅ Assertion Functions
function assertIsUser(value: unknown): asserts value is User {
  if (!isUser(value)) {
    throw new Error('Value is not a User');
  }
}

// ✅ Using with narrowing
function processValue(value: string | number | User) {
  if (isString(value)) {
    // value is string here
    return value.toUpperCase();
  }
  if (typeof value === 'number') {
    // value is number here
    return value.toFixed(2);
  }
  // value is User here
  return value.name;
}

🗺️ MAPPED TYPES

typescript
// Basic Mapped Type
type Nullable<T> = {
  [P in keyof T]: T[P] | null;
};

// With Key Remapping (as clause)
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type Setters<T> = {
  [K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};

// Filter by Value Type
type StringProperties<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

// Example Usage
interface User {
  id: number;
  name: string;
  email: string;
}

type UserGetters = Getters<User>;
// {
//   getId: () => number;
//   getName: () => string;
//   getEmail: () => string;
// }

❓ CONDITIONAL TYPES

typescript
// Basic Conditional
type IsString<T> = T extends string ? true : false;

// Extract element type from array
type ElementType<T> = T extends (infer E)[] ? E : T;

// Unwrap Promise
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

// Function return type extraction
type ExtractReturn<T> = T extends (...args: any[]) => infer R ? R : never;

// Distributive Conditional Types
type ToArray<T> = T extends unknown ? T[] : never;
type Result = ToArray<string | number>; // string[] | number[]

// Prevent Distribution with tuple
type ToArrayNonDist<T> = [T] extends [unknown] ? T[] : never;
type Result2 = ToArrayNonDist<string | number>; // (string | number)[]

📝 TEMPLATE LITERAL TYPES

typescript
// Event Names
type EventName = `on${Capitalize<'click' | 'focus' | 'blur'>}`;
// 'onClick' | 'onFocus' | 'onBlur'

// CSS Properties
type CSSValue = `${number}${'px' | 'em' | 'rem' | '%'}`;
const width: CSSValue = '100px'; // ✅
const height: CSSValue = '50%';  // ✅

// Route Parameters
type Route = `/${string}` | `/${string}/${string}`;

// Object Path Types
type PathKeys<T, Prefix extends string = ''> = T extends object
  ? {
      [K in keyof T & string]: T[K] extends object
        ? `${Prefix}${K}` | PathKeys<T[K], `${Prefix}${K}.`>
        : `${Prefix}${K}`;
    }[keyof T & string]
  : never;

⚠️ ANTI-PATTERNS

❌ Avoid any

typescript
// ❌ BAD: any defeats type safety
function process(data: any) {
  return data.value; // No type checking
}

// ✅ GOOD: Use unknown + narrowing
function processSafe(data: unknown) {
  if (isValidData(data)) {
    return data.value; // Type-safe
  }
  throw new Error('Invalid data');
}

❌ Avoid Non-null Assertion

typescript
// ❌ BAD: Can throw at runtime
const user = getUser()!;

// ✅ GOOD: Handle null explicitly
const user = getUser();
if (!user) {
  throw new Error('User not found');
}
// user is non-null here

❌ Avoid @ts-ignore

typescript
// ❌ BAD: Hides real type errors
// @ts-ignore
const value = problematicFunction();

// ✅ GOOD: Fix the type or use @ts-expect-error with comment
// @ts-expect-error: Legacy function returns wrong type, will fix in v2
const value = problematicFunction();

❌ Avoid Type Assertions When Narrowing Works

typescript
// ❌ BAD: Forces type
const element = document.getElementById('app') as HTMLDivElement;

// ✅ GOOD: Narrow with guards
const element = document.getElementById('app');
if (element instanceof HTMLDivElement) {
  // element is HTMLDivElement here
}

🎨 GENERICS PATTERNS

Generic Constraints

typescript
// Constrain to objects with specific shape
function getProperty<T extends object, K extends keyof T>(
  obj: T,
  key: K
): T[K] {
  return obj[key];
}

// Constrain to constructables
function createInstance<T extends new (...args: any[]) => any>(
  Constructor: T,
  ...args: ConstructorParameters<T>
): InstanceType<T> {
  return new Constructor(...args);
}

Generic Defaults

typescript
interface Repository<T, ID = string> {
  find(id: ID): Promise<T | null>;
  save(entity: T): Promise<T>;
}

// Uses default ID type
class UserRepository implements Repository<User> {
  // ID is string
}

// Override default
class ProductRepository implements Repository<Product, number> {
  // ID is number
}

Variadic Tuple Types

typescript
function concat<T extends unknown[], U extends unknown[]>(
  arr1: [...T],
  arr2: [...U]
): [...T, ...U] {
  return [...arr1, ...arr2];
}

const result = concat([1, 2], ['a', 'b']);
// Type: [number, number, string, string]

📎 QUICK REFERENCE

PatternWhen to Use
interfaceObject shapes, extensible contracts
typeUnions, intersections, computed types
Pick<T, K>Need subset of properties
Omit<T, K>Need all except some properties
Partial<T>Update/patch operations
Required<T>Ensure all fields present
ReturnType<T>Extract function return
Parameters<T>Extract function params
Type GuardRuntime type narrowing
Discriminated UnionState machines, result types
Mapped TypeTransform all properties
Conditional TypeType-level conditionals

🔗 RELATED SKILLS