typescript

Installation
SKILL.md

TypeScript Skill

TypeScript patterns, type safety, and best practices for frontend and backend development.

When to Use This Skill

  • Defining types and interfaces
  • Using generics effectively
  • Type narrowing and guards
  • Utility types
  • Error handling with types

📐 Type Definitions

Interface vs Type

// Interface: Prefer for object shapes, can be extended
interface User {
  id: string;
  name: string;
  email: string;
}

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

// Type: Prefer for unions, tuples, primitives
type Status = 'pending' | 'active' | 'inactive';
type Result<T> = { success: true; data: T } | { success: false; error: string };
type Coordinates = [number, number];

Strict Typing

// ✅ Good: Explicit types
interface ApiResponse<T> {
  success: boolean;
  data: T;
  meta: {
    page: number;
    total: number;
  };
}

// ✅ Good: Const assertions for literals
const CONFIG = {
  apiUrl: 'https://api.example.com',
  timeout: 5000,
} as const;

// ❌ Bad: Using `any`
function processData(data: any) { }

// ✅ Good: Use `unknown` and type guards
function processData(data: unknown) {
  if (isValidData(data)) {
    // data is now typed
  }
}

🔧 Generics

Basic Generics

// Generic function
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

// Generic interface
interface Repository<T> {
  findById(id: string): Promise<T | null>;
  findAll(): Promise<T[]>;
  create(item: Omit<T, 'id'>): Promise<T>;
  update(id: string, item: Partial<T>): Promise<T>;
  delete(id: string): Promise<void>;
}

// Generic class
class ApiClient<T> {
  constructor(private baseUrl: string) {}

  async get(endpoint: string): Promise<T> {
    const res = await fetch(`${this.baseUrl}${endpoint}`);
    return res.json();
  }
}

Constraints

// Constrain to specific shape
interface HasId {
  id: string;
}

function findById<T extends HasId>(items: T[], id: string): T | undefined {
  return items.find(item => item.id === id);
}

// Multiple constraints
function merge<T extends object, U extends object>(a: T, b: U): T & U {
  return { ...a, ...b };
}

Generic Utilities

// Pick specific keys
type UserPreview = Pick<User, 'id' | 'name'>;

// Omit specific keys
type CreateUserInput = Omit<User, 'id' | 'createdAt'>;

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

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

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

// Record type
type UserRoles = Record<string, 'admin' | 'user' | 'guest'>;

🛡️ Type Guards

Type Predicates

interface Cat {
  type: 'cat';
  meow(): void;
}

interface Dog {
  type: 'dog';
  bark(): void;
}

type Animal = Cat | Dog;

// Type predicate
function isCat(animal: Animal): animal is Cat {
  return animal.type === 'cat';
}

function handleAnimal(animal: Animal) {
  if (isCat(animal)) {
    animal.meow();  // TypeScript knows it's Cat
  } else {
    animal.bark();  // TypeScript knows it's Dog
  }
}

Discriminated Unions

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

function handleResult<T>(result: Result<T>) {
  if (result.success) {
    console.log(result.data);  // T
  } else {
    console.error(result.error);  // Error
  }
}

Assertion Functions

function assertDefined<T>(value: T | null | undefined): asserts value is T {
  if (value === null || value === undefined) {
    throw new Error('Value must be defined');
  }
}

function processUser(user: User | null) {
  assertDefined(user);
  // user is now User, not User | null
  console.log(user.name);
}

🎯 Advanced Patterns

Mapped Types

// Make all properties nullable
type Nullable<T> = {
  [K in keyof T]: T[K] | null;
};

// Make all properties promises
type Async<T> = {
  [K in keyof T]: Promise<T[K]>;
};

// Prefix all keys
type Prefixed<T, P extends string> = {
  [K in keyof T as `${P}${string & K}`]: T[K];
};

type PrefixedUser = Prefixed<User, 'user_'>;
// { user_id: string; user_name: string; user_email: string }

Conditional Types

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

type StringArray = string[];
type Element = ArrayElement<StringArray>;  // string

// Exclude null/undefined
type NonNullable<T> = T extends null | undefined ? never : T;

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

Template Literal Types

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

type HttpMethod = 'get' | 'post' | 'put' | 'delete';
type Endpoint = `/${string}`;
type Route = `${Uppercase<HttpMethod>} ${Endpoint}`;
// 'GET /...' | 'POST /...' | ...

📦 React TypeScript Patterns

Component Props

interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  loading?: boolean;
  disabled?: boolean;
  onClick?: () => void;
  children: React.ReactNode;
}

// With HTML attributes
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'primary' | 'secondary';
  loading?: boolean;
}

export function Button({ 
  variant = 'primary',
  loading,
  children,
  ...props 
}: ButtonProps) {
  return (
    <button disabled={loading} {...props}>
      {loading ? <Spinner /> : children}
    </button>
  );
}

Generic Components

interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
  keyExtractor: (item: T) => string;
}

export function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <ul>
      {items.map(item => (
        <li key={keyExtractor(item)}>{renderItem(item)}</li>
      ))}
    </ul>
  );
}

// Usage
<List
  items={users}
  renderItem={user => <span>{user.name}</span>}
  keyExtractor={user => user.id}
/>

Hooks Types

// useState with complex type
const [user, setUser] = useState<User | null>(null);

// useReducer
type Action = 
  | { type: 'SET_USER'; payload: User }
  | { type: 'LOGOUT' };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'SET_USER':
      return { ...state, user: action.payload };
    case 'LOGOUT':
      return { ...state, user: null };
  }
}

// Custom hook return type
function useUser(): {
  user: User | null;
  loading: boolean;
  login: (credentials: Credentials) => Promise<void>;
  logout: () => void;
} {
  // ...
}

🔒 Strict Configuration

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true
  }
}

📚 References

Weekly Installs
1
GitHub Stars
11
First Seen
Mar 3, 2026
Installed on
mcpjam1
claude-code1
replit1
junie1
windsurf1
zencoder1