Notorious Public Menace

    @skarab/ts-pojo-error
    TypeScript icon, indicating that this package has built-in type declarations

    1.4.0 • Public • Published

    🔥 Type safe pojo error will help you to easily create typed and serializable error.

    Intro

    The problem with exceptions is that once caught you don't know what type they are. You can of course create a bunch of custom error classes and use instanceof to overcome this. The advantage of ts-pojo-error is that you have a single error type PojoError which can be easily typed and serialized.

    Features

    • Type safe & autocompletion
    • Serializable output
    • Stackable errors
    • Node or Browser
    • ESM or CJS
    • Well tested

    Usage

    Installation

    pnpm add @skarab/ts-pojo-error # yarn and npm also works

    Defining an error factory

    • factory( errors: PojoErrorTypes ) : PojoFactory

    An error factory is constructed from a Record where the key is the type of the error and the value is a callback that defines the PojoError. The callback parameters define the parameters passed at the creation of the error and the return value defines the data of the error.

    // errors.ts
    
    import { factory } from "@skarab/ts-pojo-error";
    
    export const errors = factory({
      UNKNOWN: () => ({ message: "Unknown error..." }),
      WARNING: (message: string) => ({ message, time: Date.now() }),
      FATAL: (message?: string) => ({ message: message ?? "Fatal error" }),
      EXIT: (message: string, code: number) => ({ message, code }),
    });

    Instantiating & Throwing errors

    • new( type: infered, ...args: infered[] ) : PojoError
    • throw( type: infered, ...args: infered[] ) : never

    The first parameter is always the type of error, the following parameters are the ones you set in the factory.

    All parameters have support for autocompletion.

    // action.ts
    
    import { errors } from "./errors";
    
    export function action() {
      // Do your awsome stuff...
    
      // ...something go wrong, throw an typed error
      throw errors.new("FATAL");
    
      // or with a custom error message
      throw errors.new("FATAL", "Oupsy!");
    
      // or by using the .throw helper
      errors.throw("FATAL");
    
      // or by using the fake enum
      errors.throw(errors.type.FATAL);
    }

    Catching & Typing errors

    • is( type: infered, error: unknown ) : boolean
    • has( error: unknown ) : boolean

    This is where it gets really interesting, the problem with exceptions is that once caught you don't know what type they are. You can of course create a bunch of custom error classes and use instanceof to overcome this. The advantage of ts-pojo-error is that you have a single error type PojoError which can be easily typed and serialized.

    The is method is a type guard that will narrow the error to the specific type if the original type is compatible.

    In the if block all properties are typed and have support for autocompletion.

    // index.ts
    
    import { action } from "./action";
    import { errors } from "./errors";
    
    try {
      action();
    } catch (error) {
      error; // <- unknown type
    
      if (errors.is("FATAL", error)) {
        error; // <- PojoError instance with type "FATAL"
    
        error.message; // "Oupsy!": string
    
        error.type; // "FATAL": "FATAL"
        error.args; // ["Oupsy!"] : [message?: string | undefined]
        error.data; // { message: "Oupsy!" }: { message: string }
    
        error.cause; // Error | undefined (see "Stacking of errors" below)
    
        error.toObject(); // { type, args, data, stack?: string | undefined }
        error.toJSON(); // string
      }
    
      if (errors.has(error)) {
        error.type; // "UNKNOWN" | "WARNING" | "FATAL" | ...
      }
    
      if (error instanceof PojoError) {
        error.type; // any (Bad!)
      }
    }

    Stacking of errors

    • newFrom( cause: Error, type: infered, ...args: infered[] ) : PojoError
    • throwFrom( cause: Error, type: infered, ...args: infered[] ) : never

    Basically it adds a .cause property with the parent error to the newly created PojoError, see Error Cause tc39 proposal for further information.

    try {
      throw myErrors.new("UNKNOWN");
    } catch (error) {
      throw myErrors.newFrom(error, "FATAL");
    }

    You can stack any type of error.

    try {
      throw new Error("Unknown error");
    } catch (error) {
      myErrors.throwFrom(error, "FATAL");
    }

    Usage with voxpelli/pony-cause

    This library coded by @voxpelli includes a couple of helpers inspired by VError, supporting both standard causes and VError causes.

    import { stackWithCauses } from "pony-cause";
    
    const error1 = myErrors.new("UNKNOWN");
    const erorr2 = myErrors.newFrom(error1, "FATAL");
    const error4 = myErrors.newFrom(error3, "WARNING", "Attention to danger !!!");
    const error3 = myErrors.newFrom(
      erorr2,
      "PAGE_NOT_FOUND",
      "http://www.prout.com",
    );
    
    console.log("We had a mishap:", stackWithCauses(error4));

    To make the example more readable I have replaced the full stack with ... but the actual output contains it.

    We had a mishap: PojoError: Page Not Found: http://www.prout.com
        at index.ts:191:31
        at ...
    caused by: PojoError: Attention to danger !!!
        at index.ts:190:31
        at ...
    caused by: PojoError: Fatal error
        at index.ts:189:31
        at ...
    caused by: PojoError: Unknown error...
        at index.ts:188:34
        at ...

    Contributing 💜

    See CONTRIBUTING.md

    Install

    npm i @skarab/ts-pojo-error

    DownloadsWeekly Downloads

    5

    Version

    1.4.0

    License

    MIT

    Unpacked Size

    33.4 kB

    Total Files

    8

    Last publish

    Collaborators

    • skarab