Marcus Nguyen
Software Engineer
In real-world apps, errors can come from:
We want to:
Use custom classes to differentiate known and unknown errors.
class DefinedError extends Error {
code: string;
constructor(message: string, code: string = 'DEFINED_ERROR') {
super(message);
this.name = 'DefinedError';
this.code = code;
}
}
class UnexpectedError extends Error {
constructor(message: string = 'An unexpected error occurred') {
super(message);
this.name = 'UnexpectedError';
}
}
Hereβs how you can throw and wrap errors based on what goes wrong:
async function riskyOperation(): Promise<string> {
try {
// Simulate known failure
const shouldFail = Math.random() > 0.5;
if (shouldFail) {
throw new DefinedError('Invalid operation: Something went wrong.', 'INVALID_ACTION');
}
// Simulate success
return 'Success!';
} catch (error) {
if (error instanceof DefinedError) {
throw error; // Forward known error
}
// Wrap unexpected issues
throw new UnexpectedError(error instanceof Error ? error.message : String(error));
}
}
try {
const result = await riskyOperation();
console.log('β
Result:', result);
} catch (error) {
if (error instanceof DefinedError) {
console.warn(`[Handled] ${error.code}: ${error.message}`);
// Show user-friendly message
} else if (error instanceof UnexpectedError) {
console.error(`[Unexpected] ${error.message}`);
// Report to Sentry or monitoring tools
} else {
console.error('[Unknown error type]', error);
}
}
If you want to centralize your logic:
function handleError(error: unknown) {
if (error instanceof DefinedError) {
console.warn(`[Handled] ${error.code}: ${error.message}`);
} else if (error instanceof UnexpectedError) {
console.error(`[Unexpected] ${error.message}`);
} else {
console.error('[Unknown error]', error);
}
}
Then just:
try {
await riskyOperation();
} catch (err) {
handleError(err);
}
β
Benefits of This Pattern
With just a few lines of setup, youβve now got: