AppError
Type : class
The AppError class is designed to manage and handle errors of a specific type in a structured way within an application. It provides mechanisms to collect, report multiple errors, while also supporting error scopes for more granular error management. The class includes methods to push new errors, aggregate errors, and convert them to a string representation. It also provides functionality to catch and throw errors, with custom options for error handling behavior.
The main use case for this class is to create a central place to handle errors in an application, making it easier to manage and report errors in a consistent and structured way.
Let’s take an example without using the AppError class:
if (!user.email) // handle error;if (!user.password) // handle error;... other validation checksNow the problem is that we need to do the same thing for all other functions that might throw errors like this example:
try {  validateUserData(user);  const isUserExist = await isUserExist(user);  if (isUserExist) {'do something'};  const user = await createUser(user);} catch (error) {  'handle errors'}Each of the functions used in the previous example might throw it’s own errors, and if we don’t handle each of them separately we don’t really know which error is the one that actually happened. and it will be difficult to handle if we need to return all related errors together to the client.
Now let’s use the AppError class:
import { AppError } from '@mustib/utils'
const appError = new AppError();if (!user.email) appError.push('Validation', 'email is required');if (!user.password) appError.push('Validation', 'password is required')... other validation checksappError.end()AppError.aggregate(async (appError) => {    await appError.catch(() => {      validateUserData(user);    });
    const isUserExist = await isUserExist(user);
    if (isUserExist) appError.push('Duplication', 'user already exist');    else await appError.catch(async () => {      await createUser(user)    })})Now we only need to handle the AppError instance, and we can return all related errors together to the client like this.
try {  ... implementations} catch (error) {  if (error instanceof AppError) {    return error.toString({      includesScope: ['user', 'Validation'],      excludesScope: ['secret scope'],      });  } else {'send generic error message'}}With that we only need to handle errors in a central place.
Definition
export class AppError<ErrorTypes extends Capitalize<string>> extends Error {}Generics
- ErrorTypes- a capitalized string union of error types that are allowed to be pushed to the error instance- For Example
type ErrorTypes = 'Validation' | 'Duplication';const appError = new AppError<ErrorTypes>();appError.push('Validation', 'email is required');
 
- For Example
Constructor()
type AppError = new (options?: ErrorOptions): AppError- parameters:
- options?:ErrorOptions
 
Properties
length
visibility : protected
Type : number
default: : 0
Indicates the number of error types present in the instance
message
visibility : public
Type : string
- a getter that returns the error message containing all errors
errors
visibility : protected
type errors = {[key in ErrorTypes]?: { message: string; scope?: ErrorScope }[]}- an object that contains all errors of the instance. The keys of the object are
the error types, and the values are arrays of objects with the following
properties:
- message: the error message
- scope?: the scope of the error
 
Methods
throw()
visibility : public static
type throw = <ErrorTypes extends Capitalize<string>>(    type: ErrorTypes,    error: string | string[],    options?: ErrorOptions & { pushOptions?: PushOptions },  ): neverThis method is used to create a new AppError instance, push the error to it, and throw it instead of having to it that manually.
- parameters:- type:ErrorTypes
- error:string | string[]- see push error parameter for more details
 
- options?:ErrorOptions & { pushOptions?: PushOptions }
 
Usage:
AppError.throw('Error Type', 'Error Message')aggregate()
visibility : public static
type aggregate = async <ErrorTypes extends Capitalize<string>>(    aggregateFunc: (      appError: Omit<AppError<ErrorTypes>, 'end'>,    ) => void | Promise<void>,    options?: ErrorOptions,  ): Promise<void>This method is used if you don’t want to create a new AppError instance and end it manually by calling the end() method at the end of your implementations as it does it for you.
- 
parameters:- aggregateFunc:(appError: Omit<AppError<ErrorTypes>, ‘end’>) => void | Promise<void>- a function that will be called with a new AppErrorinstance as a parameter.
- it can be async or normal function.
 
- a function that will be called with a new 
- options?:ErrorOptions
 
- 
returns:Promise<void>
Usage:
AppError.aggregate(async (appError) => {  appError.push('Error Type', 'Error Message');  appError.catch(async () => {    await doSomething();  })})catch()
visibility : public
type catch = async (catchFunc: () => void | Promise<void>): Promise<void>This method is used to catch errors thrown from other AppError instances and add them to the current AppError instance.
- 
parameters:- catchFunc:() => void | Promise<void>- it will be called inside a try catch block to catch errors thrown from other AppErrorinstances.
- it can be async or normal function.
 
- it will be called inside a try catch block to catch errors thrown from other 
 
- 
returns:Promise<void>
Usage:
const appError = new AppError();await appError.catch(async () => {  await doSomething();});push()
visibility : public
type push = (type: ErrorTypes, error: string | string[], options?: PushOptions): voidThis method is used to add new errors to the AppError instance’s errors property.
- 
parameters:- type:ErrorTypes
- error:string | string[]- string- the error message to be added
- string[]- an array of error messages to be added
 
- options?:PushOptions
 
- 
returns:void
Usage:
const appError = new AppError();appError.push('Error Type', ['Error Message 1', 'Error Message 2'], {scope: ['user']});appError.push('Another Error Type', 'Another Error Message', {scope: ['validation']});matchesScope()
visibility: protected
type matchesScope = (options: {    errScope: ErrorScope;    includesScope?: ErrorScope;    excludesScope?: ErrorScope;  }): booleanThis method check if the error can be displayed or not based on the includesScope and excludesScope provided.
It returns true if the error scope has any scope in includesScope and has no scope in excludesScope or there is no includes or excludes scope and returns false otherwise.
- parameters:- options:an object with the following properties:- errScope: ErrorScope- the scope of the error to check
- includesScope?: ErrorScope- an optional array of scopes to include
- excludesScope?: ErrorScope- an optional array of scopes to exclude
 
 
- returns:boolean
toString()
visibility : public
type toString = (options?: Omit<      Parameters<AppError<ErrorTypes>['matchesScope']>['0'],      'errScope'    >,  ): stringThis method is used to get a string representation of the AppError instance.
- 
parameters:- options?:the same as the matchesScope method, but without the- errScopeproperty as it will be the scope of the current error.
 
- 
returns:string
Usage:
const appError = new AppError();const allErrors = appError.toString();const validationErrors = appError.toString({includesScope: ['validation']});const otherErrors = appError.toString({excludesScope: ['validation']});throw()
visibility : protected
type throw = (): neverthrows the AppError instance even if there are no errors.
- parameters:undefined
- returns:never
end()
visibility : public
type end (): voidthrows the AppError instance if there are errors.
- parameters:undefined
- returns:void
Usage:
const appError = new AppError();appError.end();ErrorScope
type ErrorScope = (string | symbol)[] | undefinedThe error scope is an array of strings and/or symbols that can be used to filter errors based on their scope.
ErrorOptions
type ErrorOptions = {  indentation?: number;  stackTraceConstructor?: Func;};- 
indentation- used to indent the error message for better readability
- default value is: 4
 
- 
stackTraceConstructor- used as Error.captureStackTrace(error, stackTraceConstructor)when throwing an error
- default value is: AppError.prototype.throw
 
- used as 
PushOptions
type PushOptions = { scope?: ErrorScope }- 
scope- See ErrorScope for more details