亚洲日本永久一区二区_国产精品k频道网址导航_首页aⅴ色老汉中文字幕_免费深夜全片观看_9久久9毛片又大又硬又粗_国产精品成亚洲电影_日韩不用播放器的av_欧美特色特黄视频

TypeScript 規(guī)范項(xiàng)目錯(cuò)誤處理

TypeScript 規(guī)范項(xiàng)目錯(cuò)誤處理

在 JavaScript 開(kāi)發(fā)中,通常都不太重視起錯(cuò)誤處理,捕獲和記錄錯(cuò)誤對(duì)于任何項(xiàng)目的開(kāi)發(fā)周期都是至關(guān)重要的。隨著 TypeScript 項(xiàng)目開(kāi)發(fā)多了,開(kāi)始意識(shí)到并不真正了解錯(cuò)誤處理。經(jīng)常在項(xiàng)目代碼中看到一下類(lèi)似代碼:

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

errorunknown 類(lèi)型 ,因此在將其轉(zhuǎn)換為新類(lèi)型或縮小類(lèi)型范圍之前,不能對(duì) error 執(zhí)行任何操作。正確的處理方式是縮小類(lèi)型,將看看如何做到這一點(diǎn),但為什么這是必要的?

在 JavaScript 中,幾乎任何東西都可以被拋出:

throw "oops"
throw 210
throw null
throw { message: "異常錯(cuò)誤" }

所以真正被捕獲的錯(cuò)誤是未知的。但是,可以通過(guò)使用 TypeScript 的多種方式干凈地處理錯(cuò)誤。

JavaScript錯(cuò)誤的基礎(chǔ)知識(shí)

JavaScript 中的錯(cuò)誤類(lèi)型,在 JavaScript 中有許多類(lèi)型的錯(cuò)誤,但最常見(jiàn)的是:

  • ReferenceError:代碼引用了一個(gè)不存在的變量。
  • TypeError:值不是預(yù)期的錯(cuò)誤類(lèi)型
  • SyntaxError:代碼在語(yǔ)法上無(wú)效

拋出錯(cuò)誤

有時(shí)需要手動(dòng)拋出錯(cuò)誤,例如,可能有一些代碼依賴(lài)于函數(shù)調(diào)用的返回值,但有可能該值是 undefined,或者至少在 TypeScript 認(rèn)為是 undefined。在下面這個(gè)例子中,拋出是縮小返回用戶范圍的最佳解決方案。

// 通常方式
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 })
}

捕獲錯(cuò)誤

一旦錯(cuò)誤被拋出,它將在調(diào)用堆棧中冒泡,直到被 try/catch 語(yǔ)句捕獲。當(dāng)在 try 塊內(nèi)運(yùn)行的代碼拋出錯(cuò)誤時(shí),它將在 catch 塊中被捕獲,錯(cuò)誤可能源自嵌套在函數(shù)內(nèi)部的函數(shù),并且會(huì)冒泡直到被捕獲。

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

縮小錯(cuò)誤類(lèi)型

一旦被捕獲,檢查所拋出的錯(cuò)誤類(lèi)型可能很有用。這使能夠?qū)㈩?lèi)型從未知縮小到可以與之交互的特定類(lèi)型(可以直觀的理解錯(cuò)誤),可以用 instanceof 做到這一點(diǎn):

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

設(shè)計(jì)模式

設(shè)計(jì)模式是軟件設(shè)計(jì)中常見(jiàn)問(wèn)題的解決方案,這些模式很容易重復(fù)使用并且富有表現(xiàn)力。在最新的項(xiàng)目中,將代碼按域分組在名為 Features 的目錄中,它可以包含相關(guān)的組件、鉤子、類(lèi)型、錯(cuò)誤等等,每個(gè) Feature 目錄都包含一個(gè) errors.ts 文件,在其中為各自的域定義了一個(gè)自定義錯(cuò)誤類(lèi)。

創(chuàng)建自定義錯(cuò)誤類(lèi)型

errors.ts 文件中,導(dǎo)出了一個(gè) class。為潛在名稱(chēng)維護(hù)一個(gè)聯(lián)合類(lèi)型,這增加了一些不錯(cuò)的智能感知和類(lèi)型安全。該類(lèi)擴(kuò)展了 Error 對(duì)象,它允許插入堆棧跟蹤(對(duì)于大多數(shù) JS 運(yùn)行時(shí))。

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;
    }
}

拋出自定義錯(cuò)誤

實(shí)例化新錯(cuò)誤時(shí),name 值具有智能感知,并且必須是聯(lián)合類(lèi)型中定義的名稱(chēng)之一。

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;
}

捕獲自定義錯(cuò)誤

當(dāng)錯(cuò)誤被捕獲時(shí),可以使用 instanceof 縮小錯(cuò)誤類(lèi)型。一旦縮小范圍,error.name 就會(huì)智能感知,此時(shí)可以根據(jù)拋出的錯(cuò)誤名稱(chēng)執(zhí)行邏輯。在此示例中,PROJECT_LIMIT_REACHED 錯(cuò)誤是要向用戶顯示的錯(cuò)誤,提供了一條專(zhuān)門(mén)為用戶呈現(xiàn)的消息。

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

定義可重用的錯(cuò)誤庫(kù)

由于項(xiàng)目中有很多 errors.ts 文件,類(lèi)中唯一的動(dòng)態(tài)代碼是名稱(chēng)的聯(lián)合類(lèi)型,因此可以對(duì)代碼進(jìn)行優(yōu)化,創(chuàng)建了一個(gè) ErrorBase 類(lèi),它接受用作名稱(chēng)類(lèi)型的泛型。

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
    }
}

現(xiàn)在,當(dāng)創(chuàng)建一個(gè)新的自定義錯(cuò)誤類(lèi)時(shí),可以擴(kuò)展這個(gè)基類(lèi),需要做的就是給它提供可用名稱(chēng)的聯(lián)合類(lèi)型。

import { ErrorBase } from "./error-base"
type ErrorName =
    'GET_PROJECT_ERROR' | 'CREATE_PROJECT_ERROR' | 'PROJECT_LIMIT_REACHED';

export class TeamError extends ErrorBase<ErrorName>{ }

總結(jié)

設(shè)計(jì)模式讓代碼變得更容易維護(hù),處理錯(cuò)誤只是維護(hù)良好的應(yīng)用程序的一部分,另一個(gè)重要步驟是使用類(lèi)似 Sentry 的工具記錄跟蹤錯(cuò)誤。