Pressidian
花园入口
笔记
项目
关于
实验室
GitHub
花园入口
笔记
项目
关于
实验室
GitHub

KNOWLEDGE PATHS

笔记库
当前位置
笔记库/前端/项目笔记/代达罗斯/Anatomy/cli

自定义错误类型与构造函数参数属性语法

2 分钟阅读 · Note

目录树 578 篇

              • 自定义错误类型与构造函数参数属性语法
            • 定义
            • 交叉类型 & 与联合类型
            • 数组转联合
            • Anatomy包
            • Extract
            • IIFE写法的工厂函数
            • TS类型 extends
          • 项目待做
          • 性能优化
          • UI设计
      • 前端技术栈
    • 笔记目录
    • CLAUDE.md
    • Vue 组件与 Render 函数

关联笔记 6

↗定义共同主题↗二、`anatomy-tree.ts`:树的构造、查询和修改共同主题↗交叉类型 & 与联合类型共同主题↗数组转联合共同主题↗Anatomy包共同主题↗Extract共同主题
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
  1. 声明一个公开属性 path
  2. 将构造函数参数赋值给 this.path
  3. 设置为只读,初始化后不能重新赋值

例如:

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