I'm taking another swing at
artifacts for its 6th season. This
time around, I'm coding it entirely in TypeScript. So naturally, I've run into
multiple async bugs from forgetting to await
promises.
The no-floating-promises eslint check can "Require Promise-like statements to be handled appropriately" and help mitigate this problem by alerting you in your editor.
This essentially requires us to use or explicitly
void
promises we don't await
.
Back in 2017, kduffie filed microsoft/TypeScript issue 13376 asking for exactly this. See discussion there.
Yeah, I recently moved over to using Deno as the JS runtime for my projects. Deno has built-in linting and formatting, but it doesn't natively support a no-floating-promises lint rule.
Specifically, Deno lint doesn't support any lint checks that require knowing type information (deno_lint issue 1138).
So we have to fallback to using eslint.
This section has my notes on setting eslint up with Deno (my runtime environment) and VS Code (my editor).
There isn't clean interop with Deno and existing eslint rules (deno_lint issue 25). So we have to set it up ourselves.
First, add a minimal tsconfig.json
(if you don't have one already) and get rid
of the compilerOptions from your deno.json
config, since eslint can't use
those. Something like:
{
"compilerOptions": {
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
}
}
You can also include some of the Deno defaults.
In this minimal config, we add a target library of ES2023
so that when we run
eslint later, it knows that we can use the latest and greatest JS features.
Otherwise we might get errors for using anything added after ES5. See the
tsconfig docs for details.
Next, update your deno.json
to include "nodeModulesDir": "auto"
as
recommended in the
Deno docs.
Then, add an eslint.config.js
that provides the configuration for eslint
itself. Something like the following to get started:
import eslint from '@eslint/js';
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
export default defineConfig(
eslint.configs.recommended,
tseslint.configs.recommendedTypeChecked,
{
languageOptions: {
parserOptions: {
projectService: {
allowDefaultProject: ["eslint.config.js"],
defaultProject: "tsconfig.json",
},
},
},
rules: {
'@typescript-eslint/no-floating-promises': 'error',
},
}
);
As mentioned before, no-floating-promises uses type information, so we have to set up our config to include that. See typed linting in the typescript-eslint docs for details.
Finally, in VS Code, install the ESLint extension and you should start seeing some errors!
Unfortunately, that will include a few no-unsafe-call and
no-unsafe-member-access errors because of our use of the Deno global. While it
would be ideal if we could just add deno.ns
to the libraries in our compiler
options above, it is not an accepted value there (vscode_deno
issue 326). (╯°□°)╯︵ ┻━┻
To get around that, we can add a declaration file (typically suffixed with
.d.ts
) to our codebase that provides the type information for stuff from the
core Deno library. For example:
declare namespace Deno {
function readTextFile(path: string): Promise<string>;
function writeTextFile(path: string, data: string): Promise<string>;
}
And there we go. Not an ideal last step, but voilà. We now have warnings in VS Code for TS code served by the Deno runtime for floating promises. ┬─┬ノ(º_ºノ)