include & exclude
include, exclude, and files decide which source files belong to a TypeScript program. They are top-level keys in tsconfig.json (siblings of compilerOptions, not inside it), and they accept glob patterns rooted at the config file’s directory. Getting them right keeps compilation fast and focused — you compile your source but not your tests, your build output, or node_modules. This page explains each key, how globs are interpreted, the default behavior when you omit them, and how imports can pull in files you never listed.
include
include is a list of glob patterns naming the files the compiler should start from. Patterns are relative to the tsconfig.json location and support * (any characters except /), ? (one character), and **/ (any number of directories).
{
"include": ["src/**/*.ts", "src/**/*.tsx"]
}
This picks up every .ts and .tsx file anywhere under src/. If include is omitted entirely, the compiler defaults to including all TypeScript files in the project directory and its subdirectories — usually too broad, which is why most projects set it explicitly.
exclude
exclude removes files from the set that include matched. It only filters include — it never excludes a file that gets pulled in by an import. Its default value already covers the folders you almost never want to compile.
{
"include": ["src"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}
When you do not specify exclude, TypeScript defaults it to ["node_modules", "bower_components", "jspm_packages", outDir]. The moment you write your own exclude, those defaults are replaced — so remember to keep node_modules and your outDir in the list.
files
files lists individual files explicitly rather than by glob. It is useful for tiny projects or generated configs with a fixed, known entry set. Unlike exclude, an exclude pattern cannot remove anything named in files.
{
"files": ["src/main.ts", "src/types.d.ts"]
}
For most applications include globs are more practical than maintaining an explicit files array by hand. Reserve files for cases where the file set is small and deliberately fixed.
How the keys combine
The final program is computed from all three keys plus the import graph. The table summarizes their roles.
| Key | Role | Default if omitted |
|---|---|---|
files | Explicit list, always included | [] |
include | Glob patterns to include | all .ts/.tsx under project |
exclude | Filters out include matches | node_modules, outDir, etc. |
exclude subtracts only from include; files entries and import-referenced files are immune to it.
Imports can override exclude
A common confusion: excluding a file does not stop it from being compiled if another included file imports it. exclude shapes the entry points, but the compiler still follows imports from those entries wherever they lead.
{
"include": ["src"],
"exclude": ["src/legacy"]
}
// src/index.ts (included) imports from the "excluded" folder...
import { oldHelper } from "./legacy/helper";
// ➜ src/legacy/helper.ts is STILL compiled, because index.ts pulls it in.
To truly keep a folder out of the program, stop importing it from included files — or move it to a separate tsconfig via project references. exclude alone will not sever an import edge.
include/exclude/filesare top-level keys, not members ofcompilerOptions. Nesting them insidecompilerOptionsis a frequent mistake that makes them silently ignored.
Best Practices
- Set
includeexplicitly (e.g.["src"]) instead of relying on the broad default. - When you write a custom
exclude, keepnode_modulesand youroutDirin it. - Exclude test files from your build config and type-check them via a separate config if needed.
- Remember
excludefiltersincludeonly — it cannot block files reached through imports. - Use
filesfor small fixed entry sets; preferincludeglobs for everything else. - Place
include/exclude/filesat the top level, never insidecompilerOptions. - Use project references to genuinely isolate folders like
legacy/from the main program.