I am having a strange issue on TypeScript. I recently learnt about void ... operator because I need to apply it so eslint wouldn't report no-floating-promises. However this particular snippet somehow caused an issue that I cannot reproduce on 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);
}
}
}
}
VS Code would report this error:
TS7011: Function expression, which lacks return-type annotation, implicitly has an 'any' return type.
However, the issue is not reproducible on 
tsc --showConfig output:
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
}
It's also interesting that it doesn't happen to other function. This, for example, does not produce any error. It seems to be something with window.setTimeout. I found out there is something different between Function type and () => void for example):
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));
}
}
CodePudding user response:
I can reproduce your issue and, although I can't answer why this is happening, you can fix the compiler error by omitting the void operator, and chaining a catch method to the promise to satisfy ESLint:
// before
window.setTimeout(() => void this.onTickAsync(true), 200);
// after
window.setTimeout(() => this.onTickAsync(true).catch(() => {}), 200);
CodePudding user response:
See microsoft/TypeScript#36651 for an authoritative answer.
Your issue is that you have enabled the --noImplicitAny compiler option but you have not enabled the --strictNullChecks compiler option. You can set these options in the TypeScript Playground and reproduce your issue.
Aside: note that --strictNullChecks is part of the --strict family of compiler features, which is generally recommended as part of a de facto "standard" level of type safety. You're not really asking about which compiler options you should use, but do note that if you use an uncommon set of compiler options you are more likely to run into compiler behavior that is not well known to the general TypeScript community. Okay, enough about that.
So we know how to reproduce, but haven't explicitly answered why there's an error here. Let's do that now. With --strictNullChecks enabled, the void opterator produces a value of the undefined type. But with --strictNullChecks disabled, there isn't really a distinct undefined type, and the void operator produces a value of the any type. And unless you explicitly annotate that a type is any, you'll get an error under --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
As you saw, you can also get rid of the error if the return type of the void operator is given a contextual type:
function foo(cb: () => any) { }
foo(() => void 0); // okay, void 0 is contextually typed as any
And note that the Function interface is a bit strange and doesn't have a true call signature, see microsoft/TypeScript#20007, so it can't provide a contextual type:
function bar(cb: Function) { }
bar(() => void 0); // implicit any error!

