A simple utility for creating custom errors effortlessly
npm install @praha/error-factory



Using custom error classes provides several benefits:
- Consistent Error Handling: Ensures a unified error structure across your application.
- Better Debugging: Enables adding additional context such as error causes, metadata, and structured messages.
- Improved Type Safety: When using TypeScript, custom error classes allow better type checking and inference.
- Code Maintainability: Reduces redundant error-handling code and keeps error logic modular and reusable.
``bash`
npm install @praha/error-factory
You can use ErrorFactory to create custom error classes easily, reducing boilerplate code and ensuring consistent error structures across your application.
The following demonstrates the most basic usage of ErrorFactory to define and use a custom error class.
`ts
import { ErrorFactory } from '@praha/error-factory';
// Define a custom error
class NotFoundError extends ErrorFactory({
name: 'NotFoundError',
message: 'Resource not found',
}) {}
// Use the custom error
try {
throw new NotFoundError();
} catch (error) {
console.error(error.name); // "NotFoundError"
console.error(error.message); // "Resource not found"
}
`
You can pass additional options to the error constructor, such as cause, which is useful for debugging complex error chains by preserving the original error context.
`ts
class DatabaseError extends ErrorFactory({
name: 'DatabaseError',
message: 'A database error occurred',
}) {}
try {
throw new DatabaseError({ cause: new Error('Connection failed') });
} catch (error) {
console.error(error.name); // "DatabaseError"
console.error(error.message); // "A database error occurred"
console.error(error.cause); // Error: Connection failed
}
`
#### Advanced Usage: Additional Fields
You can define additional fields directly in the ErrorFactory configuration, providing a more streamlined approach to adding custom properties without extending the class.
`ts
// Define an error with custom fields
class QueryError extends ErrorFactory({
name: 'QueryError',
message: 'An error occurred while executing a query',
fields: ErrorFactory.fields<{ query: string; }>(),
}) {}
try {
throw new QueryError({
query: 'SELECT * FROM users',
cause: new Error('Syntax error'),
});
} catch (error) {
console.error(error.name); // "QueryError"
console.error(error.message); // "An error occurred while executing a query"
console.error(error.query); // "SELECT * FROM users"
console.error(error.cause); // Error: Syntax error
}
`
#### Advanced Usage: Dynamic Message Generation
You can define the message as a function that receives the custom fields as parameters, allowing for dynamic and contextual error messages based on the provided data.
`tsValidation failed for field '${field}' with value '${value}'
// Define an error with dynamic message generation
class ValidationError extends ErrorFactory({
name: 'ValidationError',
message: ({ field, value }) => ,
fields: ErrorFactory.fields<{ field: string; value: unknown; }>(),
}) {}
try {
throw new ValidationError({
field: 'email',
value: 'invalid-email',
});
} catch (error) {
console.error(error.name); // "ValidationError"
console.error(error.message); // "Validation failed for field 'email' with value 'invalid-email'"
}
`
This approach is particularly useful when you need error messages that include specific details about what went wrong, making debugging and error reporting more informative.
#### Advanced Usage: Type Narrowing with Unions
You can define multiple custom error classes and use TypeScript's type narrowing to handle them effectively based on their name property.
`ts
class NotFoundError extends ErrorFactory({
name: 'NotFoundError',
message: 'Resource not found',
}) {}
class ValidationError extends ErrorFactory({
name: 'ValidationError',
message: 'Invalid input',
}) {}
class DatabaseError extends ErrorFactory({
name: 'DatabaseError',
message: 'A database error occurred',
}) {}
type ApplicationError = NotFoundError | ValidationError | DatabaseError;
const handleError = (error: ApplicationError) => {
switch (error.name) {
case 'NotFoundError':
console.error(Handle not found: ${error.message});Handle validation error: ${error.message}
break;
case 'ValidationError':
console.error();Handle database error: ${error.message}
break;
case 'DatabaseError':
console.error();
break;
}
};
try {
throw new ValidationError();
} catch (error) {
if (error instanceof Error) {
handleError(error as ApplicationError);
}
}
`
#### Built-in Presets
@praha/error-factory provides built-in error classes for common use cases.
These presets are ready to use out of the box and follow the same patterns as custom errors created with ErrorFactory.
##### UnexpectedError
A general-purpose error for handling unexpected situations in your application.
`ts
import { UnexpectedError } from '@praha/error-factory/presets';
try {
throw new UnexpectedError();
} catch (error) {
console.error(error.name); // "UnexpectedError"
console.error(error.message); // "An unexpected error has occurred"
}
// You can also pass a cause
try {
throw new UnexpectedError({ cause: new Error('Original error') });
} catch (error) {
console.error(error.cause); // Error: Original error
}
`
##### UnreachableError
An error for marking code paths that should never be reached.
This is particularly useful with TypeScript's exhaustive type checking.
`ts
import { UnreachableError } from '@praha/error-factory/presets';
type Status = 'pending' | 'success' | 'error';
const handleStatus = (status: Status) => {
switch (status) {
case 'pending':
return 'Processing...';
case 'success':
return 'Completed';
case 'error':
return 'Failed';
default:
// TypeScript ensures all cases are handled
throw new UnreachableError();
}
};
`
Contributions, issues and feature requests are welcome.
Feel free to check issues page if you want to contribute.
Copyright © PrAha, Inc.
This project is `MIT`` licensed.