添加 void 操作时,“缺少返回类型注释的函数表达式隐式具有“任意”返回类型”

Luk*_* Vo 8 function void any typescript

我在 TypeScript 上遇到了一个奇怪的问题。我最近了解了void ...operator,因为我需要应用它所以eslint不会报告no-floating-promises然而,这个特定的代码片段以某种方式导致了我无法在 TypeScript Playground 上重现的问题:

class A {

  async a() {}

  async onTickAsync(repeat: boolean) {
    try {
      await this.a();
    } catch(e) {
      console.error(e);
    } finally {
      if (repeat) {
        window.setTimeout(() => void this.onTickAsync(true), 200);
      }
    }
  }

}
Run Code Online (Sandbox Code Playgroud)

VS Code 会报这个错误:

在此输入图像描述

TS7011:缺少返回类型注释的函数表达式隐式具有“any”返回类型。

但是,该问题在TS Playground上无法重现。VS Code 和 Playground 都使用 TypeScript 4.5.4。这是我的tsconfig.json

class A {

  async a() {}

  async onTickAsync(repeat: boolean) {
    try {
      await this.a();
    } catch(e) {
      console.error(e);
    } finally {
      if (repeat) {
        window.setTimeout(() => void this.onTickAsync(true), 200);
      }
    }
  }

}
Run Code Online (Sandbox Code Playgroud)

我知道它可以通过添加: void返回类型或删除void操作或删除来修复noImplicitAny

window.setTimeout((): void => void this.onTickAsync(true), 200);
Run Code Online (Sandbox Code Playgroud)

我想问一下:是什么原因导致这个错误呢?为什么它只发生在我的 IDE/本地而不是 Playground 上?


为了确保这不仅仅是 VS Code 造成的,我还在一个单独的终端上运行了tsc --version和:tsc

在此输入图像描述

tsc --showConfig输出:

PS C:\Users\lukev\Downloads\Temp> tsc --showConfig
{
    "compilerOptions": {
        "noImplicitAny": true,
        "noEmitOnError": true,
        "sourceMap": true,
        "target": "esnext",
        "module": "esnext"
    },
    "files": [
        "./test.ts"
    ],
    "exclude": [
        "node_modules"
    ],
    "compileOnSave": true
}
Run Code Online (Sandbox Code Playgroud)

同样有趣的是,其他函数不会发生这种情况。例如,这不会产生任何错误。好像是有什么东西window.setTimeout。我发现Function类型之间有一些不同,() => void例如):

class A {

  doSomething1(_: Function) { }
  doSomething2(_: () => any) { }
  doSomething3(_: () => void) { }

  async a() { }

  async onTickAsync(repeat: boolean) {
    // Only this one produces error
    this.doSomething1(() => void this.onTickAsync(true));
    this.doSomething2(() => void this.onTickAsync(true));
    this.doSomething3(() => void this.onTickAsync(true));
  }

}
Run Code Online (Sandbox Code Playgroud)

jca*_*alz 2

请参阅microsoft/TypeScript#36651以获得权​​威答案。

您的问题是您已启用编译--noImplicitAny器选项,但尚未启用编译--strictNullChecks选项。您可以在 TypeScript Playground 中设置这些选项并重现您的问题

旁白:请注意,这--strictNullChecks编译器功能系列--strict一部分,通常建议将其作为事实上的“标准”类型安全级别的一部分。您并不是真正询问应该使用哪些编译器选项,但请注意,如果您使用一组不常见的编译器选项,您更有可能遇到一般 TypeScript 社区不太了解的编译器行为。好吧,就这些了。

所以我们知道如何重现,但还没有明确回答为什么这里会出现错误。我们现在就这样做吧。启用后--strictNullChecks运算void符会生成类型undefined值。但是对于--strictNullChecks disabled,实际上并没有明确的undefined类型,并且运算符会生成类型void值。除非您明确注释类型为,否则您将在以下情况下收到错误:anyany--noImplicitAny

// with --strictNullChecks disabled

() => void 0; // error!
// Function expression, which lacks return-type annotation, 
// implicitly has an 'any' return type.

(): undefined => void 0; // okay
//^^^^^^^^^^^ <-- arrow function return type annotation
Run Code Online (Sandbox Code Playgroud)

void正如您所看到的,如果给定运算符的返回类型为上下文类型,您也可以消除错误:

function foo(cb: () => any) { }
foo(() => void 0); // okay, void 0 is contextually typed as any
Run Code Online (Sandbox Code Playgroud)

请注意,该Function接口有点奇怪,没有真正的调用签名,请参阅microsoft/TypeScript#20007,因此它无法提供上下文类型:

function bar(cb: Function) { }
bar(() => void 0); // implicit any error!
Run Code Online (Sandbox Code Playgroud)

Playground 代码链接