Advanced TypeScript Patterns: Type-Level Programming and Beyond

April 15, 2025

Introduction

TypeScript's type system is incredibly powerful, enabling sophisticated type-level programming that can prevent runtime errors, improve developer experience, and create robust APIs. Beyond basic types and generics, TypeScript offers advanced features that allow you to build complex, type-safe abstractions.

This guide explores advanced TypeScript patterns that will elevate your type-level programming skills and help you build more maintainable, type-safe applications.

Conditional Types

Conditional types enable type logic, allowing types to change based on conditions.

Basic Conditional Types

// Basic conditional type syntax
type IsString<T> = T extends string ? true : false;

type Test1 = IsString<string>;  // true
type Test2 = IsString<number>;  // false
type Test3 = IsString<"hello">; // true

// More practical examples
type NonNullable<T> = T extends null | undefined ? never : T;

type ApiResponse<T> = T extends string
  ? { message: T }
  : T extends number
  ? { code: T }
  : { data: T };

type StringResponse = ApiResponse<string>;  // { message: string }
type NumberResponse = ApiResponse<number>;  // { code: number }
type ObjectResponse = ApiResponse<{ id: number }>; // { data: { id: number } }

Distributive Conditional Types

// Distributive conditional types work with union types
type ToArray<T> = T extends any ? T[] : never;

type StringOrNumber = string | number;
type ArrayResult = ToArray<StringOrNumber>; // string[] | number[]

// Non-distributive version
type ToArrayNonDistributive<T> = [T] extends [any] ? T[] : never;
type ArrayResultNonDist = ToArrayNonDistributive<StringOrNumber>; // (string | number)[]

// Practical example: Extract function return types
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

type FuncReturn1 = ReturnType<()=> string>;           // string
type FuncReturn2 = ReturnType<(x: number)=> number>;  // number
type FuncReturn3 = ReturnType<string>;                 // never

// Extract promise values
type Awaited<T> = T extends Promise<infer U> ? U : T;

type PromiseString = Awaited<Promise<string>>;  // string
type PlainString = Awaited<string>;             // string
type NestedPromise = Awaited<Promise<Promise<number>>>; // Promise<number>

// Recursive awaited type
type DeepAwaited<T> = T extends Promise<infer U> 
  ? DeepAwaited<U> 
  : T;

type FullyAwaited = DeepAwaited<Promise<Promise<Promise<string>>>>; // string

Template Literal Types

Template literal types enable string manipulation at the type level.

Basic Template Literals

// Basic template literal types
type Greeting = `Hello, ${string}!`;

type PersonalGreeting = `Hello, ${'Alice' | 'Bob' | 'Charlie'}!`;
// "Hello, Alice!" | "Hello, Bob!" | "Hello, Charlie!"

// HTTP methods with paths
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type ApiPath = '/users' | '/posts' | '/comments';

type ApiEndpoint = `${HttpMethod} ${ApiPath}`;
// "GET /users" | "POST /users" | "PUT /users" | "DELETE /users" | etc.

// CSS properties
type CSSUnit = 'px' | 'rem' | 'em' | '%';
type CSSValue<T extends number> = `${T}${CSSUnit}`;

type Margin = CSSValue<10>; // "10px" | "10rem" | "10em" | "10%"

Advanced String Manipulation

// Capitalize first letter
type Capitalize<S extends string> = S extends `${infer F}${infer R}`
  ? `${Uppercase<F>}${R}`
  : S;

type CapitalizedName = Capitalize<'john'>; // "John"

// Convert to camelCase
type CamelCase<S extends string> = S extends `${infer P1}_${infer P2}${infer P3}`
  ? `${P1}${Capitalize<CamelCase<`${P2}${P3}`>>}`
  : S;

type CamelCased = CamelCase<'user_name_field'>; // "userNameField"

// Extract route parameters
type ExtractRouteParams<T extends string> = 
  T extends `${string}:${infer Param}/${infer Rest}`
    ? { [K in Param | keyof ExtractRouteParams<`/${Rest}`>]: string }
    : T extends `${string}:${infer Param}`
    ? { [K in Param]: string }
    : {};

type RouteParams = ExtractRouteParams<'/users/:userId/posts/:postId'>;
// { userId: string; postId: string }

// SQL-like query builder types
type SelectClause<T> = `SELECT ${T}`;
type FromClause<T> = `FROM ${T}`;
type WhereClause<T> = `WHERE ${T}`;

type SqlQuery<S extends string, T extends string, W extends string= ''> = 
  W extends '' 
    ? `${SelectClause<S>} ${FromClause<T>}`
    : `${SelectClause<S>} ${FromClause<T>} ${WhereClause<W>}`;

type UserQuery = SqlQuery<'*', 'users', 'age > 18'>;
// "SELECT * FROM users WHERE age > 18"

Mapped Types

Mapped types create new types by transforming properties of existing types.

Basic Mapped Types

// Make all properties optional
type Partial<T> = {
  [P in keyof T]?: T[P];
};

// Make all properties required
type Required<T> = {
  [P in keyof T]-?: T[P];
};

// Make all properties readonly
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

// Create new type with different property types
type Stringify<T> = {
  [P in keyof T]: string;
};

interface User {
  id: number;
  name: string;
  email: string;
}

type StringifiedUser = Stringify<User>;
// { id: string; name: string; email: string; }

Advanced Mapped Types

// Conditional property transformation
type NullableKeys<T> = {
  [K in keyof T]: T[K] extends null | undefined ? K : never;
}[keyof T];

type NonNullableKeys<T> = {
  [K in keyof T]: T[K] extends null | undefined ? never : K;
}[keyof T];

interface MixedData {
  id: number;
  name: string | null;
  email?: string;
  age: number;
  avatar: string | null;
}

type NullableFields = NullableKeys<MixedData>; // "name" | "email" | "avatar"
type NonNullableFields = NonNullableKeys<MixedData>; // "id" | "age"

// Split type into nullable and non-nullable parts
type SplitNullable<T> = {
  required: Pick<T, NonNullableKeys<T>>;
  optional: Pick<T, NullableKeys<T>>;
};

type SplitUserData = SplitNullable<MixedData>;
// {
//   required: { id: number; age: number; };
//   optional: { name: string | null; email?: string; avatar: string | null; };
// }

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

interface NestedUser {
  id: number;
  profile: {
    name: string;
    address: {
      street: string;
      city: string;
    };
  };
}

type PartialNestedUser = DeepPartial<NestedUser>;
// {
//   id?: number;
//   profile?: {
//     name?: string;
//     address?: {
//       street?: string;
//       city?: string;
//     };
//   };
// }

Key Remapping in Mapped Types

// Remap keys with template literals
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;
};

interface Person {
  name: string;
  age: number;
}

type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number; }

type PersonSetters = Setters<Person>;
// { setName: (value: string) => void; setAge: (value: number) => void; }

// Combine getters and setters
type GettersAndSetters<T> = Getters<T> & Setters<T>;

// Filter properties by type
type FilterByType<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K];
};

interface MixedTypes {
  id: number;
  name: string;
  active: boolean;
  tags: string[];
  count: number;
}

type StringProperties = FilterByType<MixedTypes, string>;
// { name: string; }

type NumberProperties = FilterByType<MixedTypes, number>;
// { id: number; count: number; }

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

type SuffixKeys<T, S extends string> = {
  [K in keyof T as `${string & K}${S}`]: T[K];
};

type PrefixedPerson = PrefixKeys<Person, 'user_'>;
// { user_name: string; user_age: number; }

Utility Type Patterns

Advanced Utility Types

// Pick properties by value type
type PickByValueType<T, ValueType> = Pick<
  T,
  { [Key in keyof T]: T[Key] extends ValueType ? Key : never }[keyof T]
>;

interface ApiConfig {
  baseUrl: string;
  timeout: number;
  retries: number;
  debug: boolean;
  headers: Record<string, string>;
}

type StringConfig = PickByValueType<ApiConfig, string>;
// { baseUrl: string; }

type NumberConfig = PickByValueType<ApiConfig, number>;
// { timeout: number; retries: number; }

// Exclude properties by value type
type OmitByValueType<T, ValueType> = Pick<
  T,
  { [Key in keyof T]: T[Key] extends ValueType ? never : Key }[keyof T]
>;

type NonStringConfig = OmitByValueType<ApiConfig, string>;
// { timeout: number; retries: number; debug: boolean; headers: Record<string, string>; }

// Deep pick - select nested properties
type DeepPick<T, K extends string> = K extends `${infer K1}.${infer K2}`
  ? K1 extends keyof T
    ? { [P in K1]: DeepPick<T[K1], K2> }
    : never
  : K extends keyof T
  ? { [P in K]: T[K] }
  : never;

interface NestedData {
  user: {
    profile: {
      name: string;
      email: string;
    };
    settings: {
      theme: string;
      notifications: boolean;
    };
  };
  posts: Array<{ id: number; title: string; }>;
}

type UserName = DeepPick<NestedData, 'user.profile.name'>;
// { user: { profile: { name: string; } } }

// Function overload extraction
type OverloadedFunction = {
  (x: string): string;
  (x: number): number;
  (x: boolean): boolean;
};

type ExtractOverloads<T> = T extends {
  (...args: infer A1): infer R1;
  (...args: infer A2): infer R2;
  (...args: infer A3): infer R3;
} ? [
  (...args: A1) => R1,
  (...args: A2) => R2,
  (...args: A3) => R3
] : never;

type Overloads = ExtractOverloads<OverloadedFunction>;
// [
//   (x: string) => string,
//   (x: number) => number,
//   (x: boolean) => boolean
// ]

Type-Level Programming

Arithmetic Operations at Type Level

// Type-level arithmetic using tuple lengths
type Tuple<T extends number, R extends unknown[]= []> = 
  R['length'] extends T ? R : Tuple<T, [...R, unknown]>;

type Add<A extends number, B extends number> = [
  ...Tuple<A>, 
  ...Tuple<B>
]['length'];

type Subtract<A extends number, B extends number> = 
  Tuple<A> extends [...infer U, ...Tuple<B>] ? U['length'] : never;

type Sum = Add<3, 4>; // 7
type Difference = Subtract<10, 3>; // 7

// Type-level comparisons
type IsEqual<A, B> = A extends B ? B extends A ? true : false : false;
type IsNotEqual<A, B> = IsEqual<A, B> extends true ? false : true;

type Equal1 = IsEqual<string, string>; // true
type Equal2 = IsEqual<string, number>; // false

// Array operations at type level
type Head<T extends readonly unknown[]> = T extends readonly [infer H, ...unknown[]] ? H : never;
type Tail<T extends readonly unknown[]> = T extends readonly [unknown, ...infer Rest] ? Rest : [];
type Length<T extends readonly unknown[]> = T['length'];

type FirstElement = Head<[1, 2, 3, 4]>; // 1
type RestElements = Tail<[1, 2, 3, 4]>; // [2, 3, 4]
type ArrayLength = Length<[1, 2, 3, 4]>; // 4

// Reverse array
type Reverse<T extends readonly unknown[]> = T extends readonly [...infer Rest, infer Last]
  ? [Last, ...Reverse<Rest>]
  : [];

type ReversedArray = Reverse<[1, 2, 3, 4]>; // [4, 3, 2, 1]

// Type-level sorting (simplified for numbers)
type Max<A extends number, B extends number> = 
  Add<A, 0> extends Add<B, infer Diff> ? B : A;

type Sort2<A extends number, B extends number> = 
  A extends B ? [A, B] : 
  Max<A, B> extends A ? [B, A] : [A, B];

type Sorted = Sort2<5, 2>; // [2, 5]

Advanced Generic Patterns

// Generic constraint inference
type InferArrayElement<T> = T extends (infer U)[] ? U : never;
type InferPromiseType<T> = T extends Promise<infer U> ? U : never;
type InferFunctionReturn<T> = T extends (...args: any[]) => infer R ? R : never;

type ArrayElement = InferArrayElement<string[]>; // string
type PromiseValue = InferPromiseType<Promise<number>>; // number
type FunctionReturn = InferFunctionReturn<()=> boolean>; // boolean

// Multi-level inference
type DeepInfer<T> = T extends Promise<infer U>
  ? U extends (infer V)[]
    ? V extends (...args: any[]) => infer R
      ? R
      : V
    : U
  : T;

type ComplexType = DeepInfer<Promise<(()=> string)[]>>; // string

// Generic factories with constraints
interface Identifiable {
  id: string | number;
}

type CreateRepository<T extends Identifiable> = {
  findById(id: T['id']): Promise<T | null>;
  create(data: Omit<T, 'id'>): Promise<T>;
  update(id: T['id'], data: Partial<Omit<T, 'id'>>): Promise<T>;
  delete(id: T['id']): Promise<void>;
  findMany(query: Partial<T>): Promise<T[]>;
};

interface User extends Identifiable {
  id: string;
  name: string;
  email: string;
}

type UserRepository = CreateRepository<User>;
// {
//   findById(id: string): Promise<User | null>;
//   create(data: { name: string; email: string; }): Promise<User>;
//   update(id: string, data: Partial<{ name: string; email: string; }>): Promise<User>;
//   delete(id: string): Promise<void>;
//   findMany(query: Partial<User>): Promise<User[]>;
// }

Practical Advanced Patterns

State Machine Types

// Type-safe state machine
type StateDefinition = {
  [state: string]: {
    [event: string]: string;
  };
};

type StateMachine<T extends StateDefinition> = {
  [State in keyof T]: {
    state: State;
    transition<Event extends keyof T[State]>(
      event: Event
    ): StateMachine<T>[T[State][Event]];
    can<Event extends keyof T[State]>(event: Event): boolean;
    cannot<Event extends keyof T[State]>(event: Event): boolean;
  };
};

type OrderStates = {
  pending: {
    pay: 'paid';
    cancel: 'cancelled';
  };
  paid: {
    ship: 'shipped';
    refund: 'refunded';
  };
  shipped: {
    deliver: 'delivered';
    return: 'returned';
  };
  delivered: {
    return: 'returned';
  };
  cancelled: {};
  refunded: {};
  returned: {};
};

type OrderMachine = StateMachine<OrderStates>;

// Usage would be:
// const order: OrderMachine['pending'] = ...
// const paidOrder = order.transition('pay'); // Type is OrderMachine['paid']

Type-Safe Event System

// Event map definition
type EventMap = {
  user:login: { userId: string; timestamp: Date };
  user:logout: { userId: string };
  post:created: { postId: string; authorId: string; title: string };
  post:updated: { postId: string; changes: string[] };
  system:error: { error: Error; context: string };
};

// Extract event names
type EventNames = keyof EventMap;

// Type-safe event emitter
interface TypeSafeEventEmitter<T extends Record<string, any>> {
  on<K extends keyof T>(event: K, listener: (data: T[K]) => void): void;
  off<K extends keyof T>(event: K, listener: (data: T[K]) => void): void;
  emit<K extends keyof T>(event: K, data: T[K]): void;
  once<K extends keyof T>(event: K, listener: (data: T[K]) => void): void;
}

// Event emitter implementation helper
class EventEmitter<T extends Record<string, any>> implements TypeSafeEventEmitter<T> {
  private listeners = new Map<keyof T, Set<Function>>();

  on<K extends keyof T>(event: K, listener: (data: T[K]) => void): void {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set());
    }
    this.listeners.get(event)!.add(listener);
  }

  off<K extends keyof T>(event: K, listener: (data: T[K]) => void): void {
    this.listeners.get(event)?.delete(listener);
  }

  emit<K extends keyof T>(event: K, data: T[K]): void {
    this.listeners.get(event)?.forEach(listener => listener(data));
  }

  once<K extends keyof T>(event: K, listener: (data: T[K]) => void): void {
    const onceListener = (data: T[K]) => {
      listener(data);
      this.off(event, onceListener);
    };
    this.on(event, onceListener);
  }
}

// Usage
const eventEmitter = new EventEmitter<EventMap>();

eventEmitter.on('user:login', (data) => {
  // data is typed as { userId: string; timestamp: Date }
  console.log(`User ${data.userId} logged in at ${data.timestamp}`);
});

eventEmitter.emit('post:created', {
  postId: '123',
  authorId: '456',
  title: 'New Post'
  // TypeScript ensures all required properties are present
});

Database Query Builder Types

// Table schema definition
type TableSchema = {
  [tableName: string]: {
    [columnName: string]: any;
  };
};

type Schema = {
  users: {
    id: number;
    name: string;
    email: string;
    created_at: Date;
  };
  posts: {
    id: number;
    title: string;
    content: string;
    author_id: number;
    published: boolean;
  };
  comments: {
    id: number;
    post_id: number;
    author_id: number;
    content: string;
  };
};

// Query builder types
type SelectQuery<T extends TableSchema, Table extends keyof T> = {
  select<K extends keyof T[Table]>(...columns: K[]): SelectQuery<T, Table> & {
    selectedColumns: Pick<T[Table], K>;
  };
  where<K extends keyof T[Table]>(
    column: K,
    operator: '=' | '!=' | '>' | '<' | '>=' | '<=',
    value: T[Table][K]
  ): SelectQuery<T, Table>;
  join<
    JoinTable extends keyof T,
    FK extends keyof T[Table],
    PK extends keyof T[JoinTable]
  >(
    table: JoinTable,
    foreignKey: FK,
    primaryKey: PK
  ): SelectQuery<T, Table | JoinTable>;
  orderBy<K extends keyof T[Table]>(column: K, direction?: 'ASC' | 'DESC'): SelectQuery<T, Table>;
  limit(count: number): SelectQuery<T, Table>;
  execute(): Promise<T[Table][]>;
};

// Query builder factory
type QueryBuilder<T extends TableSchema> = {
  from<Table extends keyof T>(table: Table): SelectQuery<T, Table>;
};

// Usage example (pseudo-implementation)
declare const db: QueryBuilder<Schema>;

const query = db
  .from('users')
  .select('id', 'name', 'email')
  .where('created_at', '>', new Date('2023-01-01'))
  .join('posts', 'id', 'author_id')
  .orderBy('created_at', 'DESC')
  .limit(10);

// query.execute() returns Promise<Pick<Schema['users'], 'id' | 'name' | 'email'>[]>

API Route Type Safety

// API endpoint definitions
type ApiEndpoints = {
  'GET /users': {
    query?: { page?: number; limit?: number; };
    response: { id: number; name: string; email: string; }[];
  };
  'POST /users': {
    body: { name: string; email: string; };
    response: { id: number; name: string; email: string; };
  };
  'GET /users/:id': {
    params: { id: string };
    response: { id: number; name: string; email: string; } | null;
  };
  'PUT /users/:id': {
    params: { id: string };
    body: { name?: string; email?: string; };
    response: { id: number; name: string; email: string; };
  };
  'DELETE /users/:id': {
    params: { id: string };
    response: { success: boolean };
  };
};

// Extract method and path from endpoint string
type ParseEndpoint<T extends string> = T extends `${infer Method} ${infer Path}`
  ? { method: Method; path: Path }
  : never;

type EndpointInfo = ParseEndpoint<keyof ApiEndpoints>;
// { method: "GET" | "POST" | "PUT" | "DELETE"; path: "/users" | "/users/:id" }

// Type-safe API client
interface ApiClient<T extends Record<string, any>> {
  request<K extends keyof T>(
    endpoint: K,
    options: T[K] extends { params: infer P }
      ? T[K] extends { body: infer B }
        ? { params: P; body: B }
        : T[K] extends { query: infer Q }
        ? { params: P; query?: Q }
        : { params: P }
      : T[K] extends { body: infer B }
      ? { body: B }
      : T[K] extends { query: infer Q }
      ? { query?: Q }
      : {}
  ): Promise<T[K] extends { response: infer R } ? R : never>;
}

// Usage
declare const apiClient: ApiClient<ApiEndpoints>;

// Type-safe API calls
const users = await apiClient.request('GET /users', {
  query: { page: 1, limit: 10 }
});

const newUser = await apiClient.request('POST /users', {
  body: { name: 'John Doe', email: 'john@example.com' }
});

const user = await apiClient.request('GET /users/:id', {
  params: { id: '123' }
});

const updatedUser = await apiClient.request('PUT /users/:id', {
  params: { id: '123' },
  body: { name: 'Jane Doe' }
});

Performance Considerations

Type-Level Optimization

// Avoid deeply recursive types when possible
type BadDeepMerge<T, U> = T & U extends object
  ? { [K in keyof (T & U)]: BadDeepMerge<(T & U)[K], (T & U)[K]> }
  : T & U;

// Better approach with limited recursion depth
type DeepMerge<T, U, Depth extends ReadonlyArray<any> = []> = 
  Depth['length'] extends 10  // Limit recursion depth
    ? T & U
    : T extends object
    ? U extends object
      ? { [K in keyof (T & U)]: 
          K extends keyof T
            ? K extends keyof U
              ? DeepMerge<T[K], U[K], [...Depth, any]>
              : T[K]
            : K extends keyof U
            ? U[K]
            : never
        }
      : T
    : U;

// Use distributive conditional types efficiently
type EfficientFilter<T, U> = T extends U ? T : never;

// Cache complex type computations
type _ComputedTypeCache = {
  [K: string]: any;
};

// Use branded types for nominal typing
type Brand<T, B> = T & { __brand: B };
type UserId = Brand<string, 'UserId'>;
type PostId = Brand<string, 'PostId'>;

function createUser(id: UserId, name: string): void {
  // Only accepts branded UserId, not plain string
}

// Type assertion helpers
type Assert<T extends true> = T;
type Equals<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;

// Compile-time assertions
type TestUserIdIsString = Assert<Equals<UserId, string>>; // Error: false is not assignable to true

Testing Types

Type Testing Utilities

// Type testing utilities
type Expect<T extends true> = T;
type ExpectTrue<T extends true> = T;
type ExpectFalse<T extends false> = T;
type IsTrue<T extends true> = T;
type IsFalse<T extends false> = T;

type Equal<X, Y> = 
  (<T>() => T extends X ? 1 : 2) extends 
  (<T>() => T extends Y ? 1 : 2) ? true : false;

type NotEqual<X, Y> = Equal<X, Y> extends true ? false : true;

type IsAny<T> = 0 extends 1 & T ? true : false;
type IsNever<T> = [T] extends [never] ? true : false;
type IsUnknown<T> = IsAny<T> extends true ? false : unknown extends T ? true : false;

// Type tests
type TestCases = [
  // Test basic equality
  Expect<Equal<string, string>>,
  Expect<NotEqual<string, number>>,
  
  // Test utility types
  Expect<Equal<Partial<{ a: string; b: number }>, { a?: string; b?: number }>>,
  Expect<Equal<Required<{ a?: string; b?: number }>, { a: string; b: number }>>,
  
  // Test complex types
  Expect<Equal<
    ReturnType<()=> Promise<string>>,
    Promise<string>
  >>,
  
  // Test special types
  Expect<IsNever<never>>,
  Expect<IsAny<any>>,
  Expect<IsUnknown<unknown>>,
];

// Runtime type testing function
function testType<T>(): T {
  return null as any;
}

// Usage in tests
const userType = testType<User>();
const partialUserType = testType<Partial<User>>();

// Ensure types are as expected
type _Test1 = Expect<Equal<typeof userType, User>>;
type _Test2 = Expect<Equal<typeof partialUserType, Partial<User>>>;

Best Practices and Patterns

Type Organization

// Namespace organization for large type definitions
namespace Database {
  export namespace Schema {
    export interface User {
      id: number;
      name: string;
      email: string;
    }
    
    export interface Post {
      id: number;
      title: string;
      content: string;
      authorId: number;
    }
  }
  
  export namespace Operations {
    export type Create<T> = Omit<T, 'id'>;
    export type Update<T> = Partial<Omit<T, 'id'>>;
    export type Delete = { id: number };
  }
  
  export type Repository<T> = {
    create(data: Operations.Create<T>): Promise<T>;
    update(id: number, data: Operations.Update<T>): Promise<T>;
    delete(id: number): Promise<void>;
    findById(id: number): Promise<T | null>;
  };
}

// Use const assertions for immutable data structures
const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE'] as const;
type HttpMethod = typeof HTTP_METHODS[number]; // 'GET' | 'POST' | 'PUT' | 'DELETE'

const API_ENDPOINTS = {
  USERS: '/api/users',
  POSTS: '/api/posts',
  COMMENTS: '/api/comments'
} as const;
type ApiEndpoint = typeof API_ENDPOINTS[keyof typeof API_ENDPOINTS];

// Type-driven development pattern
interface FeatureConfig {
  name: string;
  enabled: boolean;
  settings: Record<string, any>;
}

type FeatureFlags = {
  [K in string]: FeatureConfig;
};

// Generate implementation from types
type ImplementFeature<T extends FeatureFlags> = {
  [K in keyof T]: {
    isEnabled(): boolean;
    getName(): string;
    getSettings(): T[K]['settings'];
    configure(settings: Partial<T[K]['settings']>): void;
  };
};

// Documentation through types
/**
 * Represents a paginated API response
 * @template T The type of items in the data array
 */
interface PaginatedResponse<T> {
  /** The current page items */
  data: T[];
  /** Pagination metadata */
  pagination: {
    /** Current page number (1-based) */
    page: number;
    /** Number of items per page */
    limit: number;
    /** Total number of items across all pages */
    total: number;
    /** Total number of pages */
    totalPages: number;
    /** Whether there are more pages after the current one */
    hasNext: boolean;
    /** Whether there are pages before the current one */
    hasPrev: boolean;
  };
}

Conclusion

Advanced TypeScript patterns enable powerful type-level programming that can significantly improve code quality, developer experience, and runtime safety. Key takeaways:

Core Techniques:

  • Conditional types enable type logic and branching
  • Template literal types provide string manipulation at compile time
  • Mapped types transform existing types systematically
  • Type-level programming enables complex computations in the type system

Advanced Applications:

  • API type safety - Ensure request/response contracts are maintained
  • State machines - Model complex state transitions safely
  • Query builders - Provide type-safe database interactions
  • Event systems - Create fully typed pub/sub mechanisms

Best Practices:

  • Start simple - Add complexity gradually as needed
  • Test your types - Use type testing utilities to validate behavior
  • Document complex types - Use comments and examples
  • Consider performance - Avoid deeply recursive types when possible
  • Organize thoughtfully - Use namespaces and modules for large type systems

When to Use Advanced Patterns:

  • Building libraries and frameworks
  • Creating type-safe APIs
  • Modeling complex domain logic
  • Improving developer experience
  • Preventing runtime errors through compile-time guarantees

Advanced TypeScript patterns require practice to master, but they provide immense value in creating robust, maintainable, and self-documenting code. Start with simpler patterns and gradually incorporate more advanced techniques as your understanding grows.

Remember: the goal is not to show off complex type gymnastics, but to create better, more reliable software through the power of TypeScript's type system.