Here are my favourite TSConfig options to introduce to any level of TypeScript dev:
noUncheckedIndexedAccess
is by now pretty well known. Without it, TypeScript lets you stumble into some pretty nasty runtime errors.
For instance, the code below won’t show an error, but will crash at runtime:
This is because by default (even with strict: true
), TypeScript will assume that any property in obj
will be a string, even though it could be undefined at runtime.
But with noUncheckedIndexedAccess
, we can get an error at compile time.
That’s because TypeScript forces you to check if the property exists before accessing it.
moduleDetection: force
tells TypeScript that you have zero global scripts in your project.
Without it, TypeScript will treat files without imports and exports as global scripts.
This means you get odd errors when you try to declare variables that clash with the global scope:
But with moduleDetection: force
, it’ll behave correctly.
It’s an auto-include for any modern TS project.
module
is a setting with a BUNCH of different options. But really, there are only two modern options.
NodeNext
tells TypeScript that your code will be run by Node.js.
This imposes some constraints, like needing to use specific .js
extensions for files.
And Preserve
tells TypeScript that an external bundler will handle the bundling.
This means you don’t need to specify the .js
extension.
As a guide, you should use NodeNext
when you’re transpiling with tsc
, and Preserve
the rest of the time (like using a frontend framework, or a bundler like Rollup).
Note that NodeNext
is equivalent to Node16
- so this is perfectly fine to use, too.
You can specify moduleResolution
to be Node
. This is a pretty common pattern.
But it’s a terrible idea.
Many libraries use ‘exports’ in package.json to specify multiple entry points to their package. But ‘Node’ doesn’t support this.
Kill it with fire wherever you see it:
Finally, verbatimModuleSyntax
makes TypeScript stricter with how you you use imports and exports.
In most cases, this will mean you’ll be forced to use import type
and export type
instead of import
and export
.
The way to fix this is to use import type
instead.
Type-only imports are erased at runtime - and the fewer imports you have, the less runtime code will need to be handled by your bundler.
So, a setting to enforce them is pretty handy.
If all of this feels bamboozling, you should check out my TSConfig Cheat Sheet.
I keep it updated with the latest changes to TSConfig, so you can rely on it.