TYPESCRIPT

Result Type Pattern

Type-safe error handling with Result type

TypeScriptError HandlingFunctional

Code

// Base Result type
type Result<T, E = Error> = Success<T> | Failure<E>;

class Success<T> {
  readonly success = true;
  constructor(readonly value: T) {}
  
  map<U>(fn: (value: T) => U): Success<U> {
    return new Success(fn(this.value));
  }
  
  flatMap<U, E>(fn: (value: T) => Result<U, E>): Result<U, E> {
    return fn(this.value);
  }
  
  unwrap(): T {
    return this.value;
  }
  
  unwrapOr(_default: T): T {
    return this.value;
  }
}

class Failure<E> {
  readonly success = false;
  constructor(readonly error: E) {}
  
  map<U>(_fn: (value: any) => U): Failure<E> {
    return this;
  }
  
  flatMap<U, F>(_fn: (value: any) => Result<U, F>): Failure<E> {
    return this;
  }
  
  unwrap(): never {
    throw this.error;
  }
  
  unwrapOr<T>(defaultValue: T): T {
    return defaultValue;
  }
}

// Helper functions
function success<T>(value: T): Success<T> {
  return new Success(value);
}

function failure<E>(error: E): Failure<E> {
  return new Failure(error);
}

// Try-catch wrapper
function tryCatch<T, E = Error>(
  fn: () => T,
  errorHandler?: (error: unknown) => E
): Result<T, E> {
  try {
    return success(fn());
  } __TOKEN_53__ (error) {
    return failure(errorHandler ? errorHandler(error) : error as E);
  }
}

// Async version
async function tryCatchAsync<T, E = Error>(
  fn: () => Promise<T>,
  errorHandler?: (error: unknown) => E
): Promise<Result<T, E>> {
  try {
    return success(await fn());
  } __TOKEN_60__ (error) {
    return failure(errorHandler ? errorHandler(error) : error as E);
  }
}

// Combine multiple results
function combine<T1, T2, E>(r1: Result<T1, E>, r2: Result<T2, E>): Result<[T1, T2], E>;
function combine<T1, T2, T3, E>(r1: Result<T1, E>, r2: Result<T2, E>, r3: Result<T3, E>): Result<[T1, T2, T3], E>;
function combine<E>(...results: Result<any, E>[]): Result<any[], E> {
  const values: any[] = [];
  
  __TOKEN_66__ (const result of results) {
    __TOKEN_69__ (!result.success) {
      return result as Failure<E>;
    }
    values.push(result.value);
  }
  
  return success(values);
}

// Usage examples
// Basic usage
function divide(a: number, b: number): Result<number, string> {
  __TOKEN_73__ (b === 0) {
    return failure('Division by zero');
  }
  return success(a / b);
}

const result1 = divide(10, 2);
__TOKEN_77__ (result1.success) {
  console.log('Result:', result1.value); // 5
}

// Chaining operations
const complexResult = divide(10, 2)
  .map(x => x * 3)
  .flatMap(x => divide(x, 5));

// Error handling
function parseJSONSafe<T>(json: string): Result<T, string> {
  return tryCatch(
    () => JSON.parse(json),
    (error) => `JSON parse error: ${error}`
  );
}

// Async example
async function fetchUser(id: number): Promise<Result<User, string>> {
  return tryCatchAsync(
    __TOKEN_84__ () => {
      const response = await fetch(`/api/users/${id}`);
      __TOKEN_87__ (!response.ok) throw new Error('Network error');
      return response.json();
    },
    (error) => `Failed to fetch user: ${error}`
  );
}

// Railway-oriented programming
class UserService {
  validateInput(input: any): Result<UserInput, string> {
    // validation logic
    return success(input);
  }
  
  async processUser(input: UserInput): Promise<Result<User, string>> {
    // processing logic
    return success({ id: 1, name: 'John' });
  }
  
  async createUser(rawInput: any): Promise<Result<User, string>> {
    return this.validateInput(rawInput)
      .flatMap(input => this.processUser(input));
  }
}