TypeScript 规范项目错误处理

简介: 笔记

5.png

在 JavaScript 开发中,通常都不太重视起错误处理,捕获和记录错误对于任何项目的开发周期都是至关重要的。随着 TypeScript 项目开发多了,开始意识到并不真正了解错误处理。经常在项目代码中看到一下类似代码:

try {
    throw new Error("Oops")
} catch (error) {
    console.error(error.message)
}

errorunknown 类型 ,因此在将其转换为新类型或缩小类型范围之前,不能对 error 执行任何操作。正确的处理方式是缩小类型,将看看如何做到这一点,但为什么这是必要的?

在 JavaScript 中,几乎任何东西都可以被抛出:


throw "oops"
throw 210
throw null
throw { message: "异常错误" }

所以真正被捕获的错误是未知的。但是,可以通过使用 TypeScript 的多种方式干净地处理错误。


JavaScript错误的基础知识


JavaScript 中的错误类型,在 JavaScript 中有许多类型的错误,但最常见的是:

  • ReferenceError:代码引用了一个不存在的变量。
  • TypeError:值不是预期的错误类型
  • SyntaxError:代码在语法上无效

抛出错误

有时需要手动抛出错误,例如,可能有一些代码依赖于函数调用的返回值,但有可能该值是 undefined,或者至少在 TypeScript 认为是 undefined。在下面这个例子中,抛出是缩小返回用户范围的最佳解决方案。


// 通常方式
function createProject() {
    const user = getUser();
    saveProject({ name: "", userId: user.id })
}
// 避免异常
function createProject() {
    const user = getUser();
    if (!user) {
        return;
    }
    saveProject({ name: "", userId: user.id })
}
// 最佳方式,抛出异常
function createProject() {
    const user = getUser();
    if (!user) {
        throw new ReferenceError('用户不存在')
    }
    saveProject({ name: "", userId: user.id })
}

捕获错误

一旦错误被抛出,它将在调用堆栈中冒泡,直到被 try/catch 语句捕获。当在 try 块内运行的代码抛出错误时,它将在 catch 块中被捕获,错误可能源自嵌套在函数内部的函数,并且会冒泡直到被捕获。

try {
    throw new ReferenceError();
} catch (error) {
    console.error(error)
}

缩小错误类型

一旦被捕获,检查所抛出的错误类型可能很有用。这使能够将类型从未知缩小到可以与之交互的特定类型(可以直观的理解错误),可以用 instanceof 做到这一点:


try {
    throw new ReferenceError();
} catch (error) {
    if (error instanceof ReferenceError) {
        console.error(error.message)
    }
}

设计模式


设计模式是软件设计中常见问题的解决方案,这些模式很容易重复使用并且富有表现力。在最新的项目中,将代码按域分组在名为 Features 的目录中,它可以包含相关的组件、钩子、类型、错误等等,每个 Feature 目录都包含一个 errors.ts 文件,在其中为各自的域定义了一个自定义错误类。

创建自定义错误类型

errors.ts 文件中,导出了一个 class。为潜在名称维护一个联合类型,这增加了一些不错的智能感知和类型安全。该类扩展了 Error 对象,它允许插入堆栈跟踪(对于大多数 JS 运行时)。


type ErrorName =
    'GET_PROJECT_ERROR' | 'CREATE_PROJECT_ERROR' | 'PROJECT_LIMIT_REACHED';
export class ProjectError extends Error {
    name: ErrorName;
    message: string;
    cause: any;
    constructor({ name, message, cause }: {
        name: ErrorName;
        message: string;
        cause?: any;
    }) {
        super();
        this.name = name;
        this.message = message;
        this.cause = cause;
    }
}

抛出自定义错误

实例化新错误时,name 值具有智能感知,并且必须是联合类型中定义的名称之一。

export async function createProject() {
    const { data, error } = await api.createProject();
    if (error) {
        throw new ProjectError({
            name: "CREATE_PROJECT_ERROR",
            message: "API error occurred while creating project",
            cause: error
        })
    }
    if (data.length === projectLimit) {
        throw new ProjectError({
            name: "PROJECT_LIMIT_REACHED",
            message: "Project limit has been reached."
        })
    }
    return data;
}

捕获自定义错误

当错误被捕获时,可以使用 instanceof 缩小错误类型。一旦缩小范围,error.name 就会智能感知,此时可以根据抛出的错误名称执行逻辑。在此示例中,PROJECT_LIMIT_REACHED 错误是要向用户显示的错误,提供了一条专门为用户呈现的消息。

try {
    await createProject();
} catch (error) {
    if (error instanceof ProjectError) {
        if (error.name === "PROJECT_LIMIT_REACHED") {
            toast(error.message)
        }
    }
}

定义可重用的错误库

由于项目中有很多 errors.ts 文件,类中唯一的动态代码是名称的联合类型,因此可以对代码进行优化,创建了一个 ErrorBase 类,它接受用作名称类型的泛型。

export class ErrorBase<T extends string> extends Error {
    name: T;
    message: string;
    cause: any;
    constructor({ name, message, cause }: { name: T, message: string, cause?: any }) {
        super();
        this.name = name;
        this.message = message;
        this.cause = cause
    }
}

现在,当创建一个新的自定义错误类时,可以扩展这个基类,需要做的就是给它提供可用名称的联合类型。

import { ErrorBase } from "./error-base"
type ErrorName =
    'GET_PROJECT_ERROR' | 'CREATE_PROJECT_ERROR' | 'PROJECT_LIMIT_REACHED';
export class TeamError extends ErrorBase<ErrorName>{ }

总结


设计模式让代码变得更容易维护,处理错误只是维护良好的应用程序的一部分,另一个重要步骤是使用类似 Sentry 的工具记录跟踪错误。


相关文章
|
2月前
|
前端开发 JavaScript 测试技术
从零开始搭建react+typescript+antd+redux+less+vw自适应项目
从零开始搭建react+typescript+antd+redux+less+vw自适应项目
52 0
|
4天前
|
监控 JavaScript 前端开发
【JavaScript与TypeScript技术专栏】TypeScript在JavaScript项目中的渐进式采用
【4月更文挑战第30天】TypeScript是JavaScript的超集,引入静态类型、接口等特性,提升代码安全性和可读性。在JavaScript项目中采用TypeScript可享受类型安全、社区支持及优秀工具集成等优势。渐进式采用策略包括评估项目现状、逐步引入新旧模块、编写类型定义文件、配置编译选项和编写测试用例,以提高项目质量和效率。
|
4天前
|
传感器 JavaScript 前端开发
【TypeScript技术专栏】TypeScript在大型项目中的实践与挑战
【4月更文挑战第30天】TypeScript在大型前端项目中日益流行,以其类型系统和ES6+支持提升代码安全与维护性。然而,采用 TypeScript 面临学习曲线、构建时间增加及类型推断挑战。通过最佳实践和成熟工具链(如 tsc、tslint 和 Visual Studio Code)可克服这些问题。案例如Angular、Basecamp和Slack已成功应用TypeScript。掌握TypeScript对提升开发者技能和市场竞争力至关重要。
|
4天前
|
JavaScript 前端开发 开发工具
【TypeScript 技术专栏】使用 TypeScript 重构 JavaScript 项目
【4月更文挑战第30天】TypeScript 在前端开发中日益流行,因其静态类型检查、增强代码可读性和更好的工具支持。本文讨论如何用 TypeScript 重构 JavaScript 项目,包括评估项目、安装 TypeScript 工具、逐步添加类型注解、处理兼容性问题以及解决重构中遇到的问题。重构后,代码质量、团队协作和可维护性均得到提升。通过实例分析,文章为 TypeScript 重构提供指导,助力构建更可靠的前端应用。
|
25天前
|
JavaScript 前端开发 IDE
TypeScript在大型前端项目中的价值与实践策略
【4月更文挑战第7天】本文探讨了TypeScript在大型前端项目中的价值和实践策略。 TypeScript通过静态类型检查、代码提示、接口与泛型提高代码质量和开发效率。在大型项目中,可采用逐步迁移策略,制定类型规范,利用IDE特性,并维护类型定义文件。通过CI/CD和培训分享,团队能充分发挥TypeScript优势,提升项目可维护性、可扩展性和开发效率。
20 0
|
4月前
|
前端开发 JavaScript 测试技术
Vue3+Vite+TypeScript常用项目模块详解(下)
现在无论gitee还是github,越来越多的前端开源项目采用Vue3+Vite+TypeScript+Pinia+Elementplus+axios+Sass(css预编译语言等),其中还有各种项目配置比如eslint 校验代码工具配置等等,而我们想要进行前端项目的二次开发,就必须了解会使用这些东西,所以作者写了这篇文章进行简单的介绍。
|
4月前
|
JavaScript 前端开发 API
Vue3+Vite+TypeScript常用项目模块详解
现在无论gitee还是github,越来越多的前端开源项目采用Vue3+Vite+TypeScript+Pinia+Elementplus+axios+Sass(css预编译语言等),其中还有各种项目配置比如eslint 校验代码工具配置等等,而我们想要进行前端项目的二次开发,就必须了解会使用这些东西,所以作者写了这篇文章进行简单的介绍。
Vue3+Vite+TypeScript常用项目模块详解
|
4月前
|
JavaScript
VueCli3+TypeScript3项目显示Markdown内容
VueCli3+TypeScript3项目显示Markdown内容
|
4月前
|
JavaScript
Vega-Embed 在 Vue Typescript 项目中引入报错
Vega-Embed 在 Vue Typescript 项目中引入报错
|
5月前
|
JavaScript
Vue TypeScript 项目引导页功能实现
Vue TypeScript 项目引导页功能实现