如何在 Typescript 中声明引发错误的函数

How to declare a function that throws an error in Typescript

提问人:marcXandre 提问时间:3/23/2018 最后编辑:JJJmarcXandre 更新时间:3/15/2023 访问量:135656

问:

在 Java 中,我会声明一个这样的函数:

public boolean Test(boolean test) throws Exception {
  if (test == true)
    return false;
  throw new Exception();
}

我可以在不处理异常的情况下使用此功能。

如果可能,如何在 Typescript 中做同样的事情?编译器会告诉我,如果没有 try/catch,我无法使用该函数。

打字稿

评论

1赞 Titian Cernicova-Dragomir 3/23/2018
不,这是不可能的
0赞 Niladri 3/23/2018
不确定您是否在寻找课程,请查看下面的答案Error
0赞 Jeff Mercado 3/23/2018
您可以通过修饰器对其进行注释,以指示该方法可能会引发,但没有什么可以强制执行的。每次调用该方法时,都必须检查注释以进行检查。
0赞 ruffin 8/2/2023
不是 100% 确定你的目标是什么——如果你返回一个布尔类型,你就完成了。我不相信,您无需做任何事情来“使用此函数而不处理异常”。举个例子,我想要一个函数来包装抛出错误(也就是说,该函数每次都会抛出错误,而不是你的“一半结果”),并最终决定将其标记为它返回与调用者的其他结果匹配的特定类型。 where 和 “return” 相同类型。const x = success ? handleX(x) : handleError(err)handleXhandleError

答:

167赞 Estus Flask 3/23/2018 #1

TypeScript 中没有这样的功能。仅当函数返回错误而不是引发错误时,才可以指定错误类型(这种情况很少发生,并且容易出现反模式)。

唯一相关的类型是 never。它只适用于一个函数肯定会抛出错误的情况,它不能比这更具体。它和其他任何类型一样,只要不引起类型问题,它就不会导致类型错误:

function Test(): never => {
  throw new Error();
}

Test(); // won't cause type error
let test: boolean = Test(); // will cause type error

当函数有可能返回值时,被返回类型吸收。never

可以在函数签名中指定它,但仅供参考:

function Test(test: boolean): boolean | never {
  if (test === true)
    return false;

  throw new Error();
}

它可以向开发人员提示未处理的错误是可能的(以防函数体中不清楚),但这不会影响类型检查,也不能强制;此函数的类型由键入系统考虑。try..catch(test: boolean) => boolean

评论

38赞 chharvey 11/30/2018
“被类型系统省略,转而使用返回类型”......这在技术上是不正确的。您不必在返回类型中指定的原因是,在联合中,所有其他类型都会吸收它。所以被简化为,就像如何简化为:在分离中,任何事物都会吸收。在某种程度上,是未知的对立面,未知在联盟中占主导地位,并被交叉点吸收neverneverboolean|neverbooleananything || falseanythingfalsenever
1赞 Estus Flask 11/30/2018
感谢您的通知。事实上,吸附在这里是正确的术语。
3赞 willurd 8/24/2020
@chharvey 只是一个简短的说明:不简化为(除非你的意思是表示所有真实的值,但不清楚)。如果左边是假的,那么 JavaScript 就会选择右边,而不管它的值如何。在控制台中尝试,您会看到 .anything || falseanythinganything||null || falsefalse
1赞 chharvey 8/27/2020
@willurd是的,这是一个次要的技术问题,但你是对的。我应该说简化为 .感谢您的更正。不同之处在于,这是一个在运行时短路的表达式,而是一种静态简化的类型。anything_truthy || falseanything_truthyanything_truthy || falseboolean | never
2赞 LeOn - Han Li 6/18/2021
NIce,感谢您的解决方案,它使编译器错误静音,如果我们有一个带有 的包装器错误处理函数,则迫使我们添加到返回类型中,该函数返回 TS def 未定义。neverundefinedvoidvoid vs never
24赞 sompnd 12/5/2019 #2

目前不可能。您可以查看此请求的功能: https://github.com/microsoft/TypeScript/issues/13219

1赞 Flavien Volken 4/29/2020 #3

你不能使用纯 ts (v<3.9) 我希望它将来可用。 然而,一种解决方法是可能的,它包括将可能的抛出类型隐藏在方法的签名中,然后在 catch 块中恢复这些类型。 我在这里用这个解决方法做了一个包:https://www.npmjs.com/package/ts-throwable/v/latest

用法或多或少如下:

import { throwable, getTypedError } from 'ts-throwable';
class CustomError extends Error { /*...*/ }

function brokenMethod(): number & throwable<CustomError> {
    if (Math.random() < 0.5) { return 42 };
    throw new CustomError("Boom!");
}

try {
    const answer: number = brokenMethod()
}
catch(error){
    // `typedError` is now an alias of `error` and typed as `CustomError` 
    const typedError = getTypedError(error, brokenMethod);
}

评论

2赞 Xetera 7/4/2020
只是 fyi 不是一个表达式,所以它在三元中的用法是不正确的throw
2赞 lmiguelmh 10/26/2020 #4

您可以将 JavaScript 视为 Java(未选中的异常)。 您可以扩展 JavaScript,但您必须使用 Object.setPrototypeOf 来恢复原型链,因为会破坏它。这个答案中也解释了对 setPrototypeOf 的需求。ErrorRuntimeExceptionErrorError

export class AppError extends Error {
    code: string;

    constructor(message?: string, code?: string) {
        super(message);  // 'Error' breaks prototype chain here
        Object.setPrototypeOf(this, new.target.prototype);  // restore prototype chain
        this.name = 'AppError';
        this.code = code;
    }
}

评论

1赞 Mack 8/3/2023
对于 ES2015 及更高版本,不需要进行此原型更改,其中继承自 Error 的工作方式按预期工作。如果你的转变成 ES2015+,你不必担心!
0赞 Angelos Pikoulas 9/21/2023
您能举例说明这如何解决原始问题吗?在 TypeScript 中将函数标记为抛出函数
0赞 lmiguelmh 9/22/2023
Java 中未经检查的异常是不需要在方法签名上声明(抛出异常)且不需要处理的异常。据我所知,Typescript 没有选中/未选中异常的概念。用 Java 术语来说:每个 Typescript 异常都是未选中的。
33赞 Klesun 2/18/2021 #5

您至少可以用 @throws jsdoc 标记该函数。即使它没有在打字稿编译器中提供静态分析错误,如果您尝试忽略抛出的函数,一些好的 IDE 或 linter 仍可能会报告警告......

/** 
 * @throws {Error}
 */
function someFunc() {
    if (Math.random() < 0.5) throw Error();
}
someFunc();

enter image description here

评论

1赞 Jacopo Tedeschi 1/13/2022
据您所知,有没有办法在 Visual Studio Code 上启用此类警告?这将是非常有帮助的。
7赞 Jacopo Tedeschi 1/13/2022
我检查了 eslint-plugin-jsdoc,但我的理解是 require-throws 规则只强制执行要记录在 JSDoc 中的异常。可悲的是,它没有在使用该函数的代码中强制执行 try-catch 语句,而这正是我们许多人正在寻找的。还有其他选择吗?
0赞 P Varga 3/11/2021 #6

不是 TypeScript,但 Hegel 可能会感兴趣,它是 JavaScript 的另一个类型检查器,并且具有此功能。你会写:

function Test(test: boolean): boolean | $Throws<Exception> {
  if (test)
    return false;
  throw new Exception();
}

查看 https://hegel.js.org/docs/magic-types#throwserrortype

1赞 Nico 12/13/2021 #7

关于这个话题,这似乎是一个有趣的公关 https://github.com/microsoft/TypeScript/pull/40468

此 PR 介绍:

  • 新的类型级表达式:throw type_expr。当前投掷类型 仅在实例化时引发。
  • 一种新的内在类型 TypeToString 打印类型
2赞 snnsnn 7/20/2022 #8

如其他答案所示,在打字稿中,易出错操作的返回类型是 。没有办法将函数标记为抛出,但是您可以使用实用程序类型使其更易于辨别:never

type Result<OK = any> = OK | never;

或者你可以让它更加引人注目:

type Result<OK = any, Error = never> = OK | Error;

同样,这些仅供眼睛使用,无法强制执行 try/catch 块。

如果要强制处理错误,请使用 promise。Linters 可以灌输未经处理的承诺。“typescript-eslint”有“No floating promises”规则。

https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-floating-promises.md

此外,当存在未处理的 promise 时,某些运行时会发出错误。

2赞 Toby Hobson 10/10/2022 #9

来自功能背景,我更喜欢在返回类型中指定预期错误(又名检查异常)。打字稿联合和类型保护使这变得简单:

class ValidationError {
  constructor(readonly message: string) {}

  static isInstance(err: unknown): err is ValidationError {
    if (err === undefined) return false
    if (typeof err !== 'object') return false
    if (err === null) return false
    return err instanceof ValidationError
  }
}

function toInt(num: string): number | ValidationError {
  const result = Number.parseInt(num)
  if (result === undefined) return new ValidationError(`Invalid integer ${num}`)
  return result
}

// caller
const result = toInt("a")
if (ValidationError.isInstance(result))
  console.log(result.message)
else
  console.log(`Success ${result}`)

这样,函数签名就会向其他开发人员突出显示潜在的错误。更重要的是,IDE和转译器将迫使开发人员处理它们(在大多数情况下)。例如,这将失败:

const result = toInt("a")
const doubled = result * 2

error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type