各种Promises实现中失败`reason`的打字稿类型?

时间:2015-03-26 18:21:06

标签: typescript promise type-safety

各种promise库的当前d.ts定义文件似乎放弃了提供给故障回调的数据类型。

when.d.ts:

interface Deferred<T> {
    notify(update: any): void;
    promise: Promise<T>;
    reject(reason: any): void;
    resolve(value?: T): void;
    resolve(value?: Promise<T>): void;
}

q.d.ts:

interface Deferred<T> {
    promise: Promise<T>;
    resolve(value: T): void;
    reject(reason: any): void;
    notify(value: any): void;
    makeNodeResolver(): (reason: any, value: T) => void;
}

jquery.d.ts(promise-ish):

fail(failCallback1?: JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[], ...failCallbacksN: Array<JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[]>): JQueryPromise<T>;

我在Promises/A+ spec中没有看到任何向我建议reason 无法输入的内容。

我确实在 qdts 上尝试了它,但是从'T'U的过渡发生时类型信息似乎丢失了,我不完全理解为什么必须如此 - 以及我的尝试(将'N'F类型参数机械地添加到<T>以及'O'G类型参数{{ 1}}并输入我认为它们应该是的东西,主要是<U>作为新添加的类型参数的类型。

有没有理由不能给他们自己的类型参数?是否有可以完全打字的承诺结构?

2 个答案:

答案 0 :(得分:5)

哎呀,这真的很难。这实际上是你在这里问的好问题。

让我先从

开始

是的,可以输入

完全有可能创建一个将异常考虑在内的承诺类型。当我使用键入的语言实现了一个promise库时,我开始使用Promise<T,E>类型,后来才恢复为Promise<T> - 它有效,但它并不有趣。你要求的是一个名字。

已检查的例外情况

你在这里实际要求的是要检查的异常 - 这是一个函数必须声明它可能抛出的异常类型 - 有些语言实际上是为异常执行此操作嗯......有 语言可以做到 - Java 。在Java中,当你有一个可能抛出异常的方法(RuntimeException除外)时,必须声明它:

 public T foo() throws E {}

请参阅,在Java中 - 返回类型和错误类型都是方法签名的一部分。这是一个有争议的选择,很多人觉得这很乏味。它在其他语言的开发者中非常不受欢迎,因为它迫使你写了很多狡猾的代码。

想象一下,你有一个函数返回一个promise,它使数据库连接获取URL,发出Web请求并将其写入文件。与Java相同的承诺就像:

Promise<T, FileAccessError | DatabaseError | WebRequestError | WebConnectionError | TypeError>

多次输入并不是很有趣 - 因此这些语言中的异常类型(如C#)通常是隐含的。也就是说,如果你喜欢那种风格的选择,你肯定应该这样做。它并不是真正的微不足道 - 承诺的类型已经非常复杂了:

then<T,U> :: Promise<T> -> T -> Promise<U> | U -> Promise<U>

这是then的作用 - 它接受类型为T的承诺,以及一个回调,它接受一个T并返回一个值(U)或一个值的承诺(Promise) - 并返回一个Promise(展开和转换)。实际类型更难以从那时起有第二个失败参数 - 两个参数都是可选的:

then<T,U> :: Promise<T> -> (T -> Promise<U> | U) | null) -> ((Promise<T> -> any -> Promise<U> | U) | null) -> Promise<U>

如果你添加错误处理,这真的很有趣&#34;因为所有这些步骤现在都有一个额外的错误路径:

then<T,E,U,E2> :: Promise<T,E> -> (T -> Promise<U, E2> | U) | null -> (E -> Promise<U, E2> | U) | null -> Promise<U>

基本上 - then现在有4种类型的参数,这是人们通常想要避免的事情:)尽管如此,它完全有可能。

答案 1 :(得分:3)

我认为实现这样的事情的主要挑战之一是then的参数是可选的,其返回类型取决于它们是否是函数。即使只有一个类型参数,Q.d.ts也无法正确使用它:

   then<U>(onFulfill?: (value: T)  => U | IPromise<U>, 
           onReject?: (error: any) => U | IPromise<U>, 
           onProgress?: Function):                      Promise<U>;

这表示Promise<T>.then()的返回类型为Promise<U>,但如果未指定onFulfill,则返回类型实际为Promise<T>

这甚至没有涉及onFulfillonReject都可以抛出的事实,为了确定{{1的类型'而给你五个不同的错误来源进行协调}}:

  • 如果p.then(onFulfill, onReject)未指定,则p的拒绝值
  • onReject
  • 中引发的错误
  • onFulfill
  • 返回的承诺的拒绝价值
  • onFulfill
  • 中引发的错误
  • onReject
  • 返回的承诺的拒绝价值

我很确定在TypeScript中甚至没有表达子弹2和4的方法,因为它没有检查异常。

如果我们使用同步代码进行类比,则可以很好地定义代码块的结果值。一段代码抛出的可能错误集很少(除非正如Benjamin指出的那样,你是用Java编写的)。

进一步采用这种类比,即使使用TypeScript的强类型,它甚至不提供(AFAIK)一种机制来指定捕获的异常上的类型,因此具有onReject错误类型的承诺与TypeScript处理同步代码中的错误的方式一致。

this page about that very matter上的评论部分包含一条我认为非常相关的评论:

  

根据定义,例外情况是“特殊情况”。条件,并且可能由于多种原因而发生(例如语法错误,堆栈溢出等...)。虽然这些错误中的大多数确实来自错误类型,但您调用的内容也可能会抛出任何内容。

同一页面上不支持类型异常的原因在这里也非常相关:

  

由于我们没有关于函数可能抛出什么异常的概念,因此允许在&#39; catch&#39;上进行类型注释。变量会产生很大误导 - 它不是一个例外过滤器,它不是类似安全保证的任何东西。

所以我的建议是不要尝试在类型定义中确定错误类型。例外情况本质上是无法预测的,any的类型定义已经足够难以定义。


另外需要注意的是:许多包含类似承诺的结构的强类型语言也无法表达它们可能产生的潜在错误类型。 .NET .then的结果只有一个类型参数,Scala的Task<T>也是如此。用于封装错误的Scala机制Future[T]Try[T]接口的一部分)除了继承自Future[T]之外,不会保证其产生的错误类型。所以我想说TypeScript中的单一类型参数promises是合适的。