Fragmented Thought

Why you should have 3 tsconfig.json files



Lance Gliser

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 and I just updated the build commands to use the -p ./ 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:


{ "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" } }


{ "extends": "./tsconfig.base.json", "include": [".storybook", "src", "stories"], "exclude": ["node_modules"] }

{ "extends": "./tsconfig.base.json", "include": ["src"], "exclude": ["node_modules", "**/*.test.*", "**/*.stories.*"] }