Go-like Error Handling in TypeScript
torben haack [t128n]
April 5, 2026
Errors as Values in TypeScript
Contrary to the popular opinion, I'm actually a big fan of Go's error handling. It makes error handling explicit while keeping the happy path clean.
Examples
In the examples below, example-2.ts just reads much cleaner to me.
<!-- example-1.ts -->
try {
const data = await fetchData();
return console.log("Data:", data);
} catch (error) {
if (error instanceof NetworkFailedError) return console.error("Network error:", error);
if (error instanceof ValidationFailedError) return console.error("Validation error:", error);
throw error;
}
<!-- example-2.ts -->
const data = await fetchData();
if (data instanceof NetworkFailedError) return console.error("Network error:", data);
if (data instanceof ValidationFailedError) return console.error("Validation error:", data);
return console.log("Data:", data);
It prevents deeper nesting and makes the return-types consistent. If we e.g. did not yet cover RateLimitError, data will be of type Data | RateLimitError.
Zero-Dependency Philosophy
The Zero-Dependency Philosophy advocated by Tommy D. Rossi makes it even more appealing. Adding more dependencies to a project can cause unwanted side-effects, when one of these dependencies e.g. has security issues[^1],[^2].
The core idea behind errore can be broken down to just 6 LOC
<!-- errore.ts -->
// You can write this without installing errore at all
class NotFoundError extends Error {
readonly _tag = 'NotFoundError'
constructor(public id: string) {
super(`User ${id} not found`)
}
}
async function getUser(id: string): Promise<User | NotFoundError> {
const user = await db.find(id)
if (!user) return new NotFoundError(id)
return user
}
const user = await getUser('123')
if (user instanceof Error) return user
console.log(user.name)
Discussion in the ATmosphere