没有第三方库的类型安全错误处理

时间:2018-10-24 14:59:39

标签: typescript error-handling try-catch

我了解ts中不进行类型安全错误处理的原因是因为我们缺少函数中的throws子句。因此,每当我这样做时,都会这样:

function mighThrow(input: number): void {
  if (input === 1) {
    throw new TypeError('cannot be one')
  } else if(input === 2) {
    throw new SyntaxError('invalid syntax: 2')
  }
  console.log('all good', input)
}

我无法准确输入错误:

try {
  mighThrow(1)
} catch(e) {
  // e is any, even though it could be TypeError | SyntaxError
}

问题与Promise相同,catcher functions参数是硬编码的any

interface Promise<T> {
  then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
  catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}

catch的论点是任意的。

我的问题是我该如何解决?如果我是图书馆作者,有没有一种类型安全的方法来帮助用户弄清楚他们在捕捉什么?我知道一些解决方案,例如添加Either类型并每天调用它,但这会强制现场处理错误,这只会使代码变成类似于该语言将检查异常的方式。 / p>

1 个答案:

答案 0 :(得分:2)

由于当前有no throws in TypeScript,因此任何建议都将看起来是一种解决方法。您正在寻找一种解决方案,该方案不会给想要忽略错误的用户带来任何开销,但可以使想要捕获错误的用户获得更强的错误类型。

这样的事情怎么样(需要TS3.1才能正常工作):

function mighThrow(input: number): void {
  if (input === 1) {
    throw new TypeError('cannot be one')
  } else if (input === 2) {
    throw new SyntaxError('invalid syntax: 2')
  }
  console.log('all good', input);
}
mighThrow.throws = undefined! as TypeError | SyntaxError;

在这里,我们使用了function property declaration来声明一个名为throws的幻像属性。幻像属性是在运行时实际上并不存在的属性,但是编译器认为确实存在,因此编译器会维护额外的类型信息。在这种情况下,undefined! as TypeError | SyntaxError在运行时只是undefined,但是编译器认为mighThrow具有类型throws的{​​{1}}属性。

现在,当人们想捕获错误时,他们可以使用帮助器功能:

TypeError | SyntaxError

像这样:

type ThrowsType<T> = T extends { throws: infer E } ? E : unknown;
const asTypedError = <FS extends Function[]>(e: any, ...f: FS): ThrowsType<FS[number]> => e;

这绝对不是完美的。它要求错误捕获器跟踪自己调用了哪些函数。包含多个功能的try-catch块需要将所有这些功能传递给try { const x = mighThrow(123); } catch (err) { const typedErr = asTypedError(mighThrow, err); // typedErr is now TypeError | SyntaxError if (typedErr instanceof TypeError) { typedErr; // TypeError } else { typedErr; // SyntaxError } }

asTypedError()

实际上,没有任何保证形式来说明所捕获的错误是在declare const alsoMightThrow: { throws: URIError } & ((input: string) => number); try { const x = mighThrow(alsoMightThrow("hey")); } catch (err) { const typedErr = asTypedError(err, mighThrow, alsoMightThrow); // // typedErr: TypeError | SyntaxError | URIError } 属性中声明的类型……这取决于库维护者是否正确(很难,因为有很多东西会在运行时抛出throws。相反,大多数函数将没有这样的TypeError属性,因此对于普通开发人员来说,使用这种类型的错误捕获没有太多动机。如果有人在其中放置非throws函数,则throws将输出unknown错误,几乎没有asTypedError有用。您必须教育图书馆用户如何使用此功能,此时,仅使功能document变得更容易,并且错误捕获程序可以执行自己的any检查以缩小范围instanceof。这是缺少语言功能的一种解决方法,看起来像它。 ‍♀️


哦,希望能有所帮助。祝你好运!