export interface DefinedErrorOptions { /** 默认错误消息 */ message?: string /** 错误类型标识,可配合 errorOf({ type }) 使用 */ type?: string } export interface DefinedErrorInstance extends Error { type: T options: O } export interface DefinedErrorClass { /** 默认选项 */ readonly defaultOptions: DefaultOpts | undefined /** 错误类型标识 */ readonly type: T /** 错误名称 */ readonly errorName: string new (options?: ErrorOptions & InstanceOpts): DefinedErrorInstance< T, DefaultOpts & InstanceOpts > new ( message?: string, options?: ErrorOptions & InstanceOpts ): DefinedErrorInstance readonly prototype: Error & { type: T } } /** * 方便的定义一个 Error 用来复用错误信息与选项 * - 可以预设 message 和 type,配合 errorOf 使用 * - 可以附带自定义数据到 options 参数 * * @param errorName 错误名称,如果未提供 message,则默认使用该名称作为错误消息 * @param defaultOptions 默认选项,可以预设 message 和 type,也可以附带自定义数据 * * @example * ```ts * const ErrorBadRequest = defineError("Bad Request", { status: 400 }) * * let err = new ErrorBadRequest({ path: "/api/data" }) * console.log(err.name) // "Bad Request" * console.log(err.message) // "Bad Request" * console.log(err.options) // { status: 400, path: "/api/data" } * * // 使用 is() 方法进行类型安全的判断 * if (ErrorBadRequest.is(err)) { * console.log(err.options.status) // 类型安全: number * } * ``` * * @example * ```ts * // 配合 type 使用 * const ErrorNotFound = defineError("NotFound", { type: "NOT_FOUND", status: 404 }) * * // 可以通过 errorOf 判断 * errorOf(err, { type: "NOT_FOUND" }) * // 或直接使用 is() * ErrorNotFound.is(err) * ``` */ export function defineError( errorName: string, defaultOptions?: DefinedErrorOptions & { type?: T } & DefaultOpts ): DefinedErrorClass & DefinedErrorOptions> { // 使用计算属性名让浏览器控制台正确显示类名 const ob = { [errorName]: class extends Error { type: T options: any constructor(options?: ErrorOptions & object) constructor(message?: string, options?: ErrorOptions & object) constructor(arg0?: any, arg1?: any) { let message: string | undefined let options: (ErrorOptions & object) | undefined if (typeof arg0 === "string" || arg0 === undefined) { message = arg0 options = arg1 } else { message = undefined options = arg0 } super(message ?? defaultOptions?.message ?? errorName, options) this.name = errorName this.type = defaultOptions?.type as T this.options = defaultOptions || options ? Object.assign({}, defaultOptions, options) : undefined } // 静态属性 static readonly errorName = errorName static readonly type = defaultOptions?.type as T static readonly defaultOptions = defaultOptions }, } return ob[errorName] as unknown as DefinedErrorClass< T, Omit & DefinedErrorOptions > }