Why you should have 3 tsconfig.json files
Published:

Heads up! This content is more than six months old. Take some time to verify everything still works as expected.
I've been working on a couple type of code bases in Typescript for our company
that use direct
tsconfig.json files instead of inherited
versions. I noticed a off and on but never really let it bug me that the behavior of
test.ts / spec.ts (Jest),
or stories.tsx (Storybook)
was always different than our src code.
I recall one time starting to investigate
by trying to change the exclude settings
as I knew we were ignoring those types of files via patterns. Just including those files
back into considering broke our builds though, as we changed the actual build output from
just the content of src published to dist or lib. Instead, with the exclusions removed
the build directories now included their own src and test folders. No good. With
too little time available to really dig into the issue, I dropped it. After all,
the thing compiled; it was a very minor syntax issue or ignore note in just non-production
files.
Recent work on a starting a Lerna monorepo that will offer our clients Typescript SDK's via NPM packages forced me to confront the issue more head on. That breakdown, of fighting to get NPM, TS, and Jest to all play nice is a much larger battle still ongoing. I'll post it once I get it all working.
One small tidbit I do want to pull out separately though is the concept of multiple
tsconfig.json. Each package in the monorepo required its own as the
rootDir changed.
This meant either a full copy paste (ick) or using
extends. Obviously, I choose
to inherit all the possible configurations I could from the root tsconfig.json.
In doing this I noticed that the includes and excludes were wrong of course.
This lead me to the idea of keeping a root tsconfig.base.json version with the
compilerOptions just as they suggest in the docs for extends. Each package then had
its own tsconfig.build.json and I just updated the build commands to use the
-p ./tsconfig.build.json argument where I could use exclude for spec, test, and story
files. Great! One last problem though, the IDE has no idea what my options are. There lies
your last tsconfig.json file, the one that used to be my default in the project root.
It's nothing more than a extends in most cases, but it makes IDE's and allows little tweaks.
As example, here's my current 3 usages for an API project:
tsconfig.base.json
{ "compilerOptions": { "allowJs": true, "allowSyntheticDefaultImports": true, "declaration": true, "declarationDir": "./lib", "outDir": "./lib", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "jsx": "react", "isolatedModules": false, "lib": ["dom", "dom.iterable", "esnext"], "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "skipLibCheck": true, "strict": true, "target": "es5" } }
tsconfig.json
{ "extends": "./tsconfig.base.json", "include": [".storybook", "src", "stories"], "exclude": ["node_modules"] }
tsconfig.build.json
{ "extends": "./tsconfig.base.json", "include": ["src"], "exclude": ["node_modules", "**/*.test.*", "**/*.stories.*"] }