error.ts 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. import z from "zod"
  2. export abstract class NamedError extends Error {
  3. abstract schema(): z.core.$ZodType
  4. abstract toObject(): { name: string; data: any }
  5. static hasName(error: unknown, name: string): boolean {
  6. return (
  7. typeof error === "object" && error !== null && "name" in error && (error as Record<string, unknown>).name === name
  8. )
  9. }
  10. static create<Name extends string, Data extends z.core.$ZodType>(name: Name, data: Data) {
  11. const schema = z
  12. .object({
  13. name: z.literal(name),
  14. data,
  15. })
  16. .meta({
  17. ref: name,
  18. })
  19. const result = class extends NamedError {
  20. public static readonly Schema = schema
  21. public override readonly name = name as Name
  22. constructor(
  23. public readonly data: z.input<Data>,
  24. options?: ErrorOptions,
  25. ) {
  26. super(name, options)
  27. this.name = name
  28. }
  29. static isInstance(input: any): input is InstanceType<typeof result> {
  30. return typeof input === "object" && "name" in input && input.name === name
  31. }
  32. schema() {
  33. return schema
  34. }
  35. toObject() {
  36. return {
  37. name: name,
  38. data: this.data,
  39. }
  40. }
  41. }
  42. Object.defineProperty(result, "name", { value: name })
  43. return result
  44. }
  45. public static readonly Unknown = NamedError.create(
  46. "UnknownError",
  47. z.object({
  48. message: z.string(),
  49. }),
  50. )
  51. }