As you may know, the tsconfig.json
in the root of a project directory allows developers to customize the TypeScript compiler settings beyond the default configuration. However, TypeScript provides hundreds of configuration options, which can sometimes confuse developers. In this series, I will go through the most essential settings that we should be familiar with. In this part 1, I will explain the six top-level options in the configuration file.
1. Files
{
"files": ["main.ts", "utils/helper.ts"]
}
This option accepts an array of strings, representing the specific files that we want to include in the compilation process. This option is most suitable for small projects or projects with a small number of files. For larger projects with multiple files, using the include
option is more convenient and scalable.
2. Include
{
"include": ["src/**/*.ts", "test/**/*.ts"]
}
The include
option serves a similar purpose to the files
option but offers greater flexibility. In addition to accepting filenames, it supports glob patterns, making it easy to match multiple files or directories. This makes the include
option more significantly useful than files
option mentioned above, especially for larger projects.
3. Exclude
{
"exclude":["src/example.ts", "src/**/*.spec.ts"]
}
Similar to the include
option, the exclude
option accepts both individual filenames and glob patterns. However, it instructs the TypeScript compiler to skip files that match these patterns, even if they are included by the include
option. For instance, the example above, the file src/utils/sum.spec.ts
will be ignored during compilation, even though this file matches the pattern src/**/*.ts
specified in include
option.
One important thing to note is that exclude
only affects files resolved by include
setting. It does not affect files explicitly specified by the files
option. In other words, files
settings take precedence over both exclude
and include
.
4. Extends
In a TypeScript project, we may encounter multiple TypeScript configuration files, such as tsconfig.base.json
, tsconfig.build.json
, tsconfig.node.json
, and so on. This is because TypeScript allows us to configure projects flexibly, and the extends
setting is one way to achieve this. The extends
setting allows a configuration file to inherit settings from another configuration file.
For example, consider a tsconfig.base.json
file (the base file) serves as a shared configuration and a tsconfig.build.json
file (the build file) that extends it, as shown below:
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"strict": true
}
}
This setting accepts a string, which is the path to another configuration file. This means that a configuration file can only inherit from only one other configuration file.
Let’s revisit the example to clarify how the the build file inherits settings from the base file:
For top-level properties (e.g.,
files
,include
,exclude
), the build file will override these properties in the base file if they are specified. Otherwise, it will use the base file’s valueFor nested-level properties (e.g.,
compilerOptions
), the build file will merge all its nested value with the base file’s values. If there are any properties defined in both files, the build file will take precedence.
5. References
The references
setting allows us to structure a large project into smaller parts. But why should we need to split the project into smaller parts? The main reason is separation of concerns and improves build performance. Let’s break down this TypeScript-based React project as an example:
/
├── src/
│ ├── pages
│ └── utils
├── lib/
│ ├── tsconfig.lib.json // We can name this file whatever
│ └── components
└── tsconfig.json
The lib
folder serves as a UI library that is shared and reused by the modules in src
folder. As a result, we can treat these folders separate, individual projects.
In tsconfig.lib.json
:
{
"compilerOptions": {
"composite": true
}
}
The referenced project’s lib
configuration must enable the composite
setting. When this setting is enabled, the declaration
setting is automatically turned on, and the TypeScript compiler will generate .d.ts files for type checking, without including actual implementation.
In tsconfig.json
:
{
"compilerOptions": {
"incremental": true,
},
"references": [
{ "path": "./lib"},
]
}
The tsconfig.json
is the main configuration file located at the root of the project. It references the configuration of the lib
folder (as a dependent project). When a module in src
needs to reuse a component from lib
, it will use d.ts
files generated in lib
for type checking. During the build process, the lib
project is built first, before the root project. In the future if there will be any changes in src
but not in lib
, the TypeScript compiler will not need to rebuild lib
when building the root project, which improves build time.
But the project references come with a few trade-offs. As mentioned above, the root project uses the d.ts
files of its lib
dependency. When we clone the project, these d.ts
files may be outdated. While working in the editor, we might make mistakes by failing to check the correct types.
6. CompilerOptions
The compilerOptions
setting is one of the most important because it determines how we apply TypeScript rules to our code base. And yes, the most confusing aspects often start here. We will walk through this setting in the next part of this series - [Part 2] What the tsconfig.json! - Exploring the most useful compiler options.