解决诺言时,打字稿会忽略类型

时间:2020-02-03 18:44:48

标签: typescript

我只花了大约一个小时来调试代码中的类型错误,最终将其归结为:

function foo(): Promise<string> {
  return new Promise((resolve, reject) => {
    resolve();
  });
}

我很失望Typescript没有完成其一项工作并检测到此错误。为什么不呢?

1 个答案:

答案 0 :(得分:2)

很遗憾,这是TypeScript design limitation (see microsoft/TypeScript#22040)Promise构造函数的构造签名的standard library's typing为:

new <T>(executor: (
  resolve: (value?: T | PromiseLike<T>) => void, 
  reject: (reason?: any) => void
) => void): Promise<T>;

您可以看到resolve()的自变量被声明为optional,无论undefined是否可分配给类型T。如果您想使用Promise<void>Promise<undefined>Promise<string | undefined>,或者undefined是有效的T的任何东西,那很好,因为{ 1}}与resolve()类似。但是,正如您所注意到的,如果您想要resolve(undefined),那么不幸的是Promise<string>被接受了。

可以通过调整标准库的类型来解决,但是不幸的是,previous attempts to fix this (see microsoft/TypeScript#22772)导致太多breaking changes in existing TypeScript code,因此他们并不想将此类更改实际推向上游。太糟糕了。


幸运的是,如果要在自己的代码中解决此问题,可以使用declaration merging添加优先考虑的构造函数签名重载。您使用的构造函数签名的特定版本取决于您的用例。如果您愿意,可以始终禁止零参数resolve()并要求人们写resolve()(如果他们想要的话)

resolve(undefined)

(请注意,declare global { // comment out this line if not in a module interface PromiseConstructor { new <T>(executor: ( resolve: (value: T | PromiseLike<T>) => void, // value is not optional reject: (reason?: any) => void) => void ): Promise<T>; } } // comment out this line if not in a module 在全局范围内,因此,如果代码在模块中,则需要使用global augmentation with declare global才能使以上内容起作用。如果您已经在全局范围内,则应省略PromiseConstructor块。)

或者,您可以尝试变得更加聪明,可以编写签名的版本,该签名的版本仅当declare global可分配给resolve()时允许不带参数的undefined

T

让我们使用后一种类型并检查某些示例代码的行为:

declare global { 
  type PossiblyOptional<F extends (a: any) => any> = F extends (...a: infer A) => infer R ?
    undefined extends A[0] ? (...a: Partial<A>) => R : F : never;

  interface PromiseConstructor {
    new <T>(executor: (
      resolve: PossiblyOptional<(value: T | PromiseLike<T>) => void>,
      reject: (reason?: any) => void) => void
    ): Promise<T>;
  }
} 

那更好,对吗?您的function foo(): Promise<string> { return new Promise((resolve, reject) => { resolve(); }); // error! // Expected 1 arguments, but got 0 -----> ~~~~~~~~~ } function bar(): Promise<string | undefined> { return new Promise((resolve, reject) => { resolve(); }); // okay } 实现会产生所需的错误,而返回类型为foo()的类似bar()实现却不会。


好的,希望能有所帮助;祝你好运!

Playground link to code