{
"path": "/writing/go-like-error-handling-in-typescript",
"site": "at://did:plc:m25hu5wadnbqnt47zep7xza6/site.standard.publication/self",
"$type": "site.standard.document",
"title": "Go-like Error Handling in TypeScript",
"description": "How treating errors as values will lead to cleaner code.",
"publishedAt": "2026-04-05T00:00:00.000Z",
"textContent": "[Errors as Values in TypeScript](https://errore.org/)\n\nContrary 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.\n\n## Examples\n\nIn the examples below, `example-2.ts` just reads much cleaner to me.\n\n```ts\n<!-- example-1.ts -->\ntry {\n const data = await fetchData();\n return console.log(\"Data:\", data);\n} catch (error) {\n if (error instanceof NetworkFailedError) return console.error(\"Network error:\", error);\n if (error instanceof ValidationFailedError) return console.error(\"Validation error:\", error);\n throw error;\n}\n```\n\n<br/>\n\n```ts\n<!-- example-2.ts -->\nconst data = await fetchData();\nif (data instanceof NetworkFailedError) return console.error(\"Network error:\", data);\nif (data instanceof ValidationFailedError) return console.error(\"Validation error:\", data);\nreturn console.log(\"Data:\", data);\n```\n\nIt 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`.\n\n## Zero-Dependency Philosophy\n\nThe **[Zero-Dependency Philosophy](https://errore.org/#:~:text=Zero%2DDependency%20Philosophy)** advocated by\n[Tommy D. Rossi](https://github.com/remorses) 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]<sup>,</sup>[^2].\n\nThe core idea behind errore can be broken down to just 6 LOC\n\n```ts {3,4,5,6,7,8}\n<!-- errore.ts -->\n// You can write this without installing errore at all\nclass NotFoundError extends Error {\n readonly _tag = 'NotFoundError'\n constructor(public id: string) {\n super(`User ${id} not found`)\n }\n}\n\nasync function getUser(id: string): Promise<User | NotFoundError> {\n const user = await db.find(id)\n if (!user) return new NotFoundError(id)\n return user\n}\n\nconst user = await getUser('123')\nif (user instanceof Error) return user\nconsole.log(user.name)\n```\n\n[^1]: [Post Mortem: axios npm supply chain compromise #10636](https://github.com/axios/axios/issues/10636)\n\n[^2]: [Security Update: Suspected Supply Chain Incident](https://docs.litellm.ai/blog/security-update-march-2026)"
}