Skip to content

Instantly share code, notes, and snippets.

@jaandrle
Last active July 9, 2021 16:22
Show Gist options
  • Save jaandrle/c76ae19830771270fd5539c91a63b5e3 to your computer and use it in GitHub Desktop.
Save jaandrle/c76ae19830771270fd5539c91a63b5e3 to your computer and use it in GitHub Desktop.
JavaScript: errors idea
const errors= (function(module){
return module();
})(function(){
const proxy_keys= [ "create", "isInstance" ];
const proxy_obj= { get(target, prop){ return prop===Symbol.iterator ? target[Symbol.iterator].bind(target) : target[proxy_keys.indexOf(prop)]; } };
/**
* This subclass of Error supports chaining.
* If available, it uses the built-in support for property `.cause`.
* Otherwise, it sets it up itself.
*
* @see https://github.com/tc39/proposal-error-cause
* @see https://2ality.com/2021/06/error-cause.html
*/
class CausedError extends Error {
constructor(message, options, skip_stack= 1){
super(message, options);
this.stack= this.stack.split("\n").slice(skip_stack).join("\n");
if(!isObject(options)||!hasCause(options)||hasCause(this)) return;
const { cause }= options;
this.cause= cause;
if(!Reflect.has(cause, "stack")) return;
const { stack, name, message: message_origin }= cause;
const cause_text= `\nCAUSED BY ${name}: ${message_origin}`;
Reflect.set(this, "stack", this.stack+cause_text+stack);
}
}
return { create, type, isCausedError, CausedError };
/**
* Create new error type based on `name`
* @param {string} name Type name of error
* @param {function} [messageCreator=i=>i] Message modificator
* @returns {[createThis,isInstanceThis]&{create:createThis,isInstance:isInstanceThis}}
* @example <caption>Main Usage</caption>
* const [ E, isE ]= type("Name");
* const { create, isInstance }= type("Name");
* @example
* E= type("Test", ({ name, type })=> `The ${name} is not ${type}`);
* E.create({ name: "A", type: "String" })
* //= Test: The A is not String\n…
*
* E= type("Test2");
* E.create("Message");
* //= Test2: Message\n…
*/
function type(name, messageCreator= i=>i){
/**
* @callback isInstanceThis
* @param {CustomError} _instance
* @returns {boolean}
*/
/**
* @callback createThis
* @param {string} message
* @param {ErrorOptions} [options]
* @returns {CustomError}
*/
/** Custom error */
class CustomError extends CausedError{ constructor(m,o){ super(messageCreator(m),o, 3); this.name= name; } }
return new Proxy([ (...args)=> new CustomError(...args), isInstance.bind(null, CustomError) ], proxy_obj);
}
/**
* @typedef ErrorOptions
* @type {object}
* @param {Error} [cause]
*/
/**
* Initialize Error (in fact `CausedError`)
* @param {string} message Error message
* @param {ErrorOptions} [options] Error options
* @returns {CausedError}
*/
function create(message, options){ return new CausedError(message, options, 2); }
/**
* @param {Error|CausedError} _error
* @returns {boolean}
*/
function isCausedError(_error){ return _error instanceof CausedError; }
function isInstance(_class, _instance){ return _instance instanceof _class; }
function isObject(value){ return value !== null && typeof value === "object"; }
function hasCause(target){ return Reflect.has(target, "cause"); }
});
function createError(name, messageCreator= i=>i, data= null){
class CustomError extends Error{
constructor(...args){ super(messageCreator(...args)); this.name= name; Object.assign(this, data); }
static isInstance(_Error){ return _Error instanceof CustomError; }
};
const keys= [ "create", "isInstance" ];
return new Proxy(
[ (...args)=> new CustomError(...args), CustomError.isInstance ],
{
get(target, prop, receiver){
return prop===Symbol.iterator ? target[Symbol.iterator].bind(target) : target[keys.indexOf(prop)];
}
}
);
};
const [ TestError, isTestError ]= createError("TestError");
console.log(TestError, isTestError);
const AnotherError= createError("AnotherError");
console.log(AnotherError.create, AnotherError.isInstance);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment