Search
⌘K

Ts Result

Result pattern.

Example Usage

import type {
  Result,
  ResultAlwaysSuccess,
  InferResultSuccess,
  InferResultError
} from './ts-result';

// ========================
// Result
// ========================

type Fn1Result = Result<{ name: string; }, 'FETCH_FAILED'>;

const fn1 = async (): Promise<Fn1Result> => {

  // ts force you to return a discriminated union

  try {
    const result = await fetch('https://api.example.com');

    // for error not unexpected
    if (!result.ok) {
      return {
        status: 'error',
        code: 'FETCH_FAILED',
        message: 'Fetch status is not ok: ' + result.status,
      };
    }

    // for success
    return {
      status: 'success',
      data: await result.json(),
    };
  }
  catch (error) {
    // for error unexpected
    return {
      status: 'error',
      code: 'UNKNOWN_ERROR',
      message: 'Something went wrong with fetch',
    };

  }

};

// ========================
// ResultAlwaysSuccess
// ========================

type Fn2Result = ResultAlwaysSuccess<{ name: string; }>;
const fn2 = async (): Promise<Fn2Result> => {

  // ts force you to return a value with status === 'success'
  // use this if you know that error is not possible, or if you want to treat error as success

  // ... do your work

  return {
    status: 'success',
    data: { name: 'John Doe' },
  };
};


// ========================
// Infer Types
// ========================

type Fn1ResultSuccess = InferResultSuccess<Fn1Result>;
type Fn1ResultError = InferResultError<Fn1Result>;

type Fn3Result = Result<
  Fn1ResultSuccess['data'],
  "INVALID_INPUT" | Fn1ResultError['code']
>;

const fn3 = async (text: string): Promise<Fn3Result> => {

  try {

    // if invalid input -> return `INVALID_INPUT` error code
    if (text.length < 3) {
      return {
        status: 'error',
        code: 'INVALID_INPUT',
        message: 'Invalid input',
      };
    }

    // if error in sub fn -> return sub fn error
    const fn1Result = await fn1();
    if (fn1Result.status === 'error') {
      return fn1Result;
    }

    // if success in sub fn -> return sub fn success
    return {
      status: 'success',
      data: fn1Result.data,
    };

  }
  catch (error) {
    // if error unexpected -> return `UNKNOWN_ERROR` error code
    return {
      status: 'error',
      code: 'UNKNOWN_ERROR',
      message: 'Something went wrong',
    };

  }
};

Dependencies

No dependencies

Auto Install

npx shadcn@latest add https://shadcn-registry-ts.vercel.app/r/util-ts-result.json

Manual Install

ts-result.ts
/**
 * Source: http://localhost:3000
 */


/** Create a Result Success type */
type ResultSuccess<S> = {
  status: 'success';
  data: S;
};
/** Create a Result Error type */
type ResultError<Code extends string> = {
  status: 'error';
  code: Code | 'UNKNOWN_ERROR',
  message: string;
};

/** 
 * Create a Result type.  
 * You must pass two generics:
 * - the first will be the `data` prop of `success` path.
 * - the second will be the `code` prop of `error` path.
 * */
export type Result<S, E extends string> = ResultSuccess<S> | ResultError<E>;

/** 
 * Create a Result type that cannot have `error` path.
 * You must pass one generic:
 * - will be the `data` prop of `success` path.
 * */
export type ResultAlwaysSuccess<S> = ResultSuccess<S>;


/** Utility used to infer the `success` discriminated union of a Result type */
export type InferResultSuccess<R> = Extract<R, { status: 'success'; }>;

/** Utility used to infer the `error` discriminated union of a Result type */
export type InferResultError<R> = Extract<R, { status: 'error'; }>;

Test

No test

Command Palette

Search for a command to run...