代码之家  ›  专栏  ›  技术社区  ›  Tom

在Typescript中,如何区分节点和普通Javascript错误类型?

  •  4
  • Tom  · 技术社区  · 8 年前

    我有以下功能:

    /**
     * Retrieves a component template from filesystem
     */
    const getComponentTemplate = async (
      p: string
    ): Promise<string> => {
      let template: string
      try {
        template = await fs.readFile(p, {
          encoding: 'utf8'
        })
      } catch (e) {
        if (e instanceof Error && e.code === 'ENOENT') {
          throw new Error(`template for element type ${elementType} not found`)
        }
        throw e
      }
    
      return template
    }
    

    Typescript在这里抱怨:

    [ts] Property 'code' does not exist on type 'Error'

    这是因为Javascript Error 类只有属性 message and name .

    然而,Node的 错误 班上确实有 code property .

    Typescript在一个特殊的接口中定义了这一点 ErrnoException (见资料来源) here ).我补充说 @types/node 给我的包裹。json,但这并没有让Typescript意识到 错误 这是问题的一部分 ErrnoException 界面

    不可能在catch子句中声明类型注释。那么,如何让Typescript编译器能够解决这是一个节点错误呢?

    仅供参考,这是我工作的一部分 tsconfig.json :

    {
      "compilerOptions": {
        "target": "es2017",
        "module": "commonjs",
        "lib": ["es2017"]
        ...
      }
    }
    
    3 回复  |  直到 8 年前
        1
  •  4
  •   user310988 user310988    8 年前

    如果你想使用try/catch,那么你会得到一个你不知道类型的对象。

    您已经对代码进行了测试,以查看该对象是否为 Error ,如果是,则将其转换为“正常”JS 错误 对象

    你可以用 typeguard 告诉类型系统对象的实际类型。

    大致如下:

    function isError(error: any): error is ErrnoException { return error instanceof Error; }
    

    我看了一眼 fs.readFile 使用这个函数,甚至整个节点api的一种常见方法似乎是,通过向它传递一个回调函数,在任务完成或出现错误时调用该函数。

    看看 type definition 这表明传递给回调的错误对象确实是所需的 ErrnoException .

    export function readFile(path: PathLike | number, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void;
    

    因此,使用回调将消除对类型guard的需要,而且似乎是实现这一点的节点方式。

    This article 显然,它详细说明了“收回所有东西”方法背后的一些想法。

    Node大量使用回调可以追溯到一种编程风格 比JavaScript本身更古老。连续传球方式(CPS)是 how Node的旧校名。js现在使用回调。在CPS中,一个 continuation函数(read:callback)作为参数传递给 在该代码的其余部分运行后调用。这允许 不同的功能,以异步手动控制来回 在应用程序中。

    节点。js依靠异步代码来保持速度,因此 可靠的回调模式至关重要。如果没有它,开发人员将 被困在每个人和每个人之间保持不同的签名和风格 每个模块。错误优先模式被引入节点核心,以 解决这个问题,并从那时起蔓延到今天 标准而每个用例都有不同的需求和 错误优先模式可以容纳所有的响应。

        2
  •  3
  •   Tom    8 年前

    最后我使用了@AndyJ的评论:

    /**
     * Retrieves a component template from filesystem
     */
    const getComponentTemplate = async (
      p: string
    ): Promise<string> => {
      let template: string
      try {
        template = await fs.readFile(p, {
          encoding: 'utf8'
        })
      } catch (e) {
        // tslint:disable-next-line:no-unsafe-any
        if (isNodeError(e) && e.code === 'ENOENT') {
          throw new Error(`template for element type ${elementType} not found`)
        }
        throw e
      }
    
      return template
    }
    
    /**
     * @param error the error object.
     * @returns if given error object is a NodeJS error.
     */
    const isNodeError = (error: Error): error is NodeJS.ErrnoException =>
      error instanceof Error
    

    但我惊讶地发现这是必要的。它还要求你 disable tslint's 如果你正在使用任何规则。

        3
  •  1
  •   HugoTeixeira    8 年前

    你可以考虑阅读 code 属性,然后检查其值是否等于 ENOENT :

    try {
        ...
    } catch (e) {
        const code: string = e['code'];
        if (code === 'ENOENT') {
            ...
        }
        throw e
    }
    

    这不是一个完美的解决方案,但考虑到您不能在catch子句中声明类型,并且 e instanceof ErrnoException 检查无法正常工作(如问题注释中所述)。

        4
  •  1
  •   Takeshi Tokugawa YD    4 年前

    类型安全类型脚本解决方案

    这不是一个普遍的解决方案,但对全球经济有效 ErrnoException 案例 相符合的 “@types/node”:“16.11.xx” 定义 无例外 界面是:

    interface ErrnoException extends Error {
       errno?: number | undefined;
       code?: string | undefined;
       path?: string | undefined;
       syscall?: string | undefined;
    }
    

    以下类型的守卫完全尊重这一防御。我的TypeScript和ESLint设置非常严格,因此很可能不需要注释来禁用ESLint/TSLint(如果您仍然使用这个去润滑的注释)。

    function isErrnoException(error: unknown): error is ErrnoException {
      return isArbitraryObject(error) &&
        error instanceof Error &&
        (typeof error.errno === "number" || typeof error.errno === "undefined") &&
        (typeof error.code === "string" || typeof error.code === "undefined") &&
        (typeof error.path === "string" || typeof error.path === "undefined") &&
        (typeof error.syscall === "string" || typeof error.syscall === "undefined");
    }
    

    哪里

    type ArbitraryObject = { [key: string]: unknown; };
    
    function isArbitraryObject(potentialObject: unknown): potentialObject is ArbitraryObject {
      return typeof potentialObject === "object" && potentialObject !== null;
    }
    

    现在我们可以检查 code 财产:

    import FileSystem from "fs";
    import PromisfiedFileSystem from "fs/promises";
    
    // ...
    
    let targetFileStatistics: FileSystem.Stats;
    
    try {
    
      targetFileStatistics = await PromisfiedFileSystem.stat(validAbsolutePathToPublicFile);
    
    } catch (error: unknown) {
    
      if (isErrnoException(error) && error.code === "ENOENT") {
    
         response.
             writeHead(HTTP_StatusCodes.notFound, "File not found.").
             end();
    
         return;
      }
    
     
      response.
          writeHead(HTTP_StatusCodes.internalServerError, "Error occurred.").
          end();
    }
    
    推荐文章