ยท hands on
Optimizing TypeScript Configs: Balancing Compilation and Type Checking
Learn how to optimize TypeScript configurations for lean compilation and type checking. Use rootDir and exclude patterns to manage configuration files without unnecessary compilation.
Modern web tools like Playwright, Vitest, and Webpack allow you to save configurations in TypeScript. Typically, these configuration files are located at the root of your project, while your source code resides in a separate "src" directory. Using this location approach, I faced challenges managing type checking for my configuration files without compiling them when running tsc
. Luckily, I found a solution which I want to share with you.
Contents
- Problem Statement
- Intended Behavior
- Initial TypeScript Configuration
- Defining a root directory
- Using an Exclude Pattern
- Creating a Type-Checking Configuration
- Summary
- Final Configurations
Problem Statement
I wanted type checking enabled for configuration files like playwright.config.ts
and vitest.config.ts
. By default, TypeScript resolves all files in the directory containing the tsconfig.json
file. This results in config files being checked but also compiled to JavaScript when running tsc
. Having a configuration written in a TypeScript file and its emitted JavaScript version can confuse IDE extensions. I actually encountered an issue when working with the official Vitest extension for Visual Studio Code. Additionally, placing TypeScript files outside the "src" folder leads to a separate "src" folder within the outDir when running a compilation.
Intended Behavior
I wanted type-checking reports for my configuration files without compiling them to JavaScript when running tsc
. Additionally, I wanted to avoid having a "src" directory inside my "dist" directory to maintain a flat file structure. Hereโs how I achieved this.
Initial TypeScript Configuration
Initially, my tsconfig.json
was set up like this:
My "include" pattern covered everything in the "src" folder plus the Vitest configuration file. Running tsc --noEmit
flagged issues in my source code or Vitest config but also created an unwanted dist/vitest.config.d.ts
file and "dist/src" folder.
Defining a root directory
To address this, I defined a "rootDir" so the compiler would only inspect my source code when emitting JavaScript:
Unfortunately, this led to the problem that vitest.config.ts
is outside the root directory, which the TypeScript compiler dislikes, resulting in the following message:
'rootDir' is expected to contain all source files.
The file is in the program because:
Matched by include pattern 'vitest.config.ts' in 'tsconfig.json'
Using an Exclude Pattern
That's why I used an exclude pattern to satisfy the tsc
command. I also took this opportunity to exclude other generated directories, such as documentation or code coverage reports, that the TypeScript compiler might pick up:
Now my build configuration was working, but running tsc --noEmit
didnโt report errors for files matched by the exclude pattern, such as vitest.config.ts
. While I could see errors in my IDE, I wanted them to appear when running the tsc
command.
Creating a Type-Checking Configuration
To solve the missing type-checking for my configuration files, I created another TypeScript configuration that inherits from my main configuration but resets the rootDir
and exclude
settings:
I named this file tsconfig.check.json
because I will mainly use it for type checking. It includes the noEmit
setting as well, so that I don't need to specify it anymore when running the console command. It also helps from accidental compilations.
To use my type-checking configuration, I pass it to the TypeScript compiler with the following command:
I made it part of my "scripts" section in my "package.json" file for easy access:
Summary
By using two TypeScript configurations (tsconfig.json
for compilation and tsconfig.check.json
for extended type checking), I can now enjoy all the benefits of TypeScript. This setup allows me to receive error reports for misconfigurations of Vitest and other tools while keeping my output directory clean of these configuration files. The maintenance is also minimal since one TSConfig inherits from the other.
Final Configurations
For compiling with tsc
:
For testing with tsc --project tsconfig.check.json
: