export class AnatomyDefinitionFileError extends Error {
constructor(
message: string,
public readonly path: string,
public readonly cause?: unknown,
) {
super(message);
this.name = "AnatomyDefinitionFileError";
}
}
这里的 path 和 cause 都是在给错误对象补充更多上下文信息。
export class AnatomyDefinitionFileError extends Error {
constructor(
message: string,
public readonly path: string,
public readonly cause?: unknown,
) {
super(message);
this.name = "AnatomyDefinitionFileError";
}
}
path 的作用
public readonly path: string
表示这次错误是在哪个文件路径上发生的。
例如:
throw new AnatomyDefinitionFileError(
"读取 Anatomy 定义失败",
"/project/anatomy.json",
);
捕获后可以拿到:
try {
// ...
} catch (error) {
if (error instanceof AnatomyDefinitionFileError) {
console.log(error.message);
console.log(error.path);
}
}
输出可能是:
读取 Anatomy 定义失败
/project/anatomy.json
所以 path 主要用于定位:
- 哪个文件读取失败
- 哪个配置文件格式错误
- 哪个目录操作失败
它比只写一个错误字符串更方便程序处理。
cause 的作用
public readonly cause?: unknown
表示引发这个错误的原始错误。
例如底层读取文件失败:
try {
await readFile(path);
} catch (error) {
throw new AnatomyDefinitionFileError(
"读取 Anatomy 定义文件失败",
path,
error,
);
}
这里:
error
是原始错误,可能是:
ENOENT: no such file or directory
而你对外抛出的新错误是:
AnatomyDefinitionFileError
这样就形成了两层信息:
业务错误:
读取 Anatomy 定义文件失败
原始原因:
文件不存在
捕获时可以查看:
if (error instanceof AnatomyDefinitionFileError) {
console.log(error.message);
console.log(error.path);
console.log(error.cause);
}
为什么 cause 是 unknown
因为底层抛出来的不一定是标准 Error。
JavaScript 允许抛出任何值:
throw new Error("失败");
throw "失败";
throw 123;
throw { code: "ENOENT" };
所以 TypeScript 中最安全的类型是:
unknown
使用前需要判断:
if (error.cause instanceof Error) {
console.log(error.cause.message);
}
而不是直接:
error.cause.message;
因为 cause 不一定有 message。
public readonly 做了什么
这是一种 TypeScript 的构造函数参数属性语法。
constructor(
public readonly path: string,
)
相当于:
export class AnatomyDefinitionFileError extends Error {
public readonly path: string;
constructor(message: string, path: string) {
super(message);
this.path = path;
}
}
所以这一行同时完成了三件事:
public readonly path: string
- 声明一个公开属性
path - 将构造函数参数赋值给
this.path - 设置为只读,初始化后不能重新赋值
例如:
const error = new AnatomyDefinitionFileError(
"读取失败",
"/a.json",
);
console.log(error.path); // 可以读取
error.path = "/b.json";
// 报错:readonly 属性不能修改
cause 同理:
public readonly cause?: unknown
其中 ? 表示它是可选的。
所以可以不传:
new AnatomyDefinitionFileError(
"内容格式错误",
"/a.json",
);
也可以传:
new AnatomyDefinitionFileError(
"读取失败",
"/a.json",
originalError,
);
完整使用示例
async function loadAnatomyFile(path: string) {
try {
const content = await readFile(path, "utf-8");
return JSON.parse(content);
} catch (error) {
throw new AnatomyDefinitionFileError(
"无法加载 Anatomy 定义文件",
path,
error,
);
}
}
调用:
try {
await loadAnatomyFile("/config/anatomy.json");
} catch (error) {
if (error instanceof AnatomyDefinitionFileError) {
console.error("错误信息:", error.message);
console.error("文件路径:", error.path);
if (error.cause instanceof Error) {
console.error("原始原因:", error.cause.message);
}
}
}
可以把这三个字段理解为:
message // 发生了什么业务错误
path // 在哪里发生
cause // 底层为什么发生
例如:
message: 无法加载 Anatomy 定义文件
path: /config/anatomy.json
cause: ENOENT: no such file or directory