Skip to content

Instantly share code, notes, and snippets.

@LexVocoder
Last active October 3, 2018 16:38
Show Gist options
  • Save LexVocoder/782015fcf87decd9d35789e03c3928c9 to your computer and use it in GitHub Desktop.
Save LexVocoder/782015fcf87decd9d35789e03c3928c9 to your computer and use it in GitHub Desktop.
DError - Observable-friendly errors with causes, inspired by VError ( DEPRECATED; see https://www.npmjs.com/package/yandere )
// Inspired by VError.
import { none, Option, some, option, None } from "ts-option";
import * as extsprintf from 'extsprintf';
import { defined, getType, toPrintable } from "./utilities";
interface IError {
message: string;
name: string;
stack?: string;
}
//undefined, null, none, string, array, object, boolean
export function asIError(val: any): IError {
if (!defined(val)) {
return {
message: toPrintable(val),
name: getType(val),
stack: '',
}
}
if (defined(val.message) && defined(val.name) && defined(val.stack)) {
return val;
}
return {
message: defined(val.message) ? val.message : toPrintable(val),
name: defined(val.name) ? val.name : getType(val),
stack: defined(val.stack) ? val.stack : '',
}
}
// Returns the error names, messages, and stack traces for err and its causes.
// err's stack trace appears first. This is safe to call on anything
// matching the IError interface, including plain JS Error instances.
//
export function fullStack(err: IError): string {
var suffix = '';
// `instanceof DError` was yielding false negatives
const cause: Option<IError> = (err && (<DError>err).cause) || none;
if (cause.isDefined) {
suffix = "\n" +
"/-----------\n" + // these are so hard to read otherwise
`| caused by: ` + fullStack(cause.get);
}
if (err.stack) {
return err.stack + suffix;
} else {
return `${err.name}: ${err.message}` + suffix;
}
}
// DError stands for "Deep Error". Unlike VError, this requires a cause.
// Why? Well, many things, particularly RxJS, have
// `any` types in their error handlers. VError, on the other hand,
// gets very confused when the cause is a string (or
// any other non-Error value).
//
// For documentation on formatting syntax, see `extsprintf`.
//
export class DError extends Error {
cause: Option<IError>;
constructor(cause: any, format: string, ...args: any[]) {
var myMessage = extsprintf.sprintf(format, ...args);
var fullMessage = myMessage;
const maybeCauseAsError: Option<IError> = isSomething(cause) ? some(asIError(cause)) : none;
if (maybeCauseAsError.isDefined) {
fullMessage += `: ${maybeCauseAsError.get.message}`
}
super(fullMessage);
this.cause = maybeCauseAsError;
this.name = 'DError';
}
}
// true iff val is defined, non-null, and not `none`
function isSomething(val: any): boolean {
return defined(val) && val !== null && val !== none;
}
/**
function mapUnlessIsNothing<T>(val: any, fn: (any) => T): Option<T> {
if (!isSomething(val)) {
return none;
}
return option(fn(val));
}
**/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment