Repository navigation
Allow overrides in tsconfig #33407
Description
Search Terms
- tsconfig
- overrides
- eslint
- glob
Suggestion
It would be nice if tsconfig.json files would allow an overrides section which works in the same way as ESLint. Here I have just one config file in my root for all kind of files and use cases. TypeScript currently needs multiple tsconfig.json files.
Use Cases
As far as I know editors like VS Code look for the nearest tsconfig.json for proper TypeScript support. This make it hard to properly type files which are used for different environments (e.g. source code vs tests vs storybook examples and so on) in the same directory, which seems to be a common convention nowadays (e.g. src/file.ts, src/file.test.ts, src/file.stories.ts).
Most people seem to ignore this fact which makes test globals like describe available in source code files.
I once tweeted about this with a small example:
Examples
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"strict": true,
"skipLibCheck": true,
"target": "es2017",
"types": []
},
"overrides": [
{
"include": ["src/**/*"],
"exclude": ["src/**/*.test.ts"],
"compilerOptions": {
"jsx": "react",
"types": ["webpack-env"],
"plugins": [
{
"name": "typescript-styled-plugin"
}
]
}
},
{
"include": ["src/**/*.test.ts"],
"compilerOptions": {
"types": ["jest", "node"]
}
}
]
}Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
Activity
AnyhowStep commentedon Sep 13, 2019
Maybe they shouldn't be mixing test and source files?
The
extends,files,includeandexcludeoptions can be used in most cases,https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#configuration-inheritance-with-extends
#8435 (comment)
#8435 (comment)
donaldpipowitch commentedon Sep 13, 2019
That's what I suggest as well, but I'm not the majority as far as I can tell. The community seems to prefer it differently.
donaldpipowitch commentedon Sep 14, 2019
I understand the old comment from Mohamed Hegazy (@mhegazy) that a tsconfig.json matches one tsc call. Maybe we need a tsconfig-compose.json or something similar in that case. Something which editors can automatically pick up to get the correct project configuration. Even Microsofts React Starter mixed source and test files and now recommends CRA which does the same. (It's also not just tests and sources. It can be some configuration files, storybook stories and so on mixed in the same directory.)
leethree commentedon Jun 11, 2020
It's called "colocating" and it's a common pattern in the JavaScript world.
Exhibit A:
https://islovely.co/posts/colocating-unit-tests
Exhibit B:
https://twitter.com/dan_abramov/status/762658867327172608?lang=en
donaldpipowitch commentedon Jun 12, 2020
FYI: I haven't tried it, but it could be that this problem can now be solved by having “Solution Style” tsconfig.json Files.
Something like this:
If that works it would probably make this issue obsolete.
aleclarson commentedon Nov 12, 2020
I've tried the "solution project" approach without success:
https://gist.github.com/aleclarson/6ae9a3b6b9d06c492fb9a3b6ec395411
tizmagik commentedon Mar 19, 2021
This would be super useful. Without this, we need separate tsconfig for source and test files, separate build, dev scripts etc etc. Meanwhile, our eslint config can stay in a single file and can use unified scripts because of the overrides capability allowed there.
ThomasFindlay commentedon Jul 2, 2021
Does anyone know when a solution for the problem specified by the author will be available? Like some people here mentioned, it is a common pattern to put source and test files together. Another example where defining tsconfig for specific files only would be useful is Jest + Cypress. Both of these provide global definitions for methods like expect or describe. Now with the recent addition of Cypress Components I can imagine people using Jest for unit testing methods whilst Cypress Components, as the name suggests, for components in frameworks like Vue or React.
ryankav commentedon Nov 3, 2021
I would also support the addition of this feature. Am currently migrating a codebase to typescript and it would be nice to specify strict compiler options on
.tsfiles whilst having weaker rules on the.jsones. This way the check javascript flag can be enabled immediately without having to chose between being overwhelmed by errors in the javascript files or weaker compiler settings in the newly made typescript files.As a concrete example it could be nice to allow typescript files to be written with strict mode true, yet have the javascript files include the
strictFunctionTypescompiler setting so that we can check that any newly typed functions imported to the javascript files are being used correctly immediately. If there is already a way to do this then I would appreciate any advice as I can't seem to find an explanation or working implementation for how to do this.tavoyne commentedon Jan 11, 2022
One pretty common use case I think would be in mono-repository architectures, where you have multiple packages all having their own
src,test, etc. directories. You want to be able to enforce a specific config for ALLtestfiles, ALLsrcfiles, etc. For example, you might want to enableisolatedModulesforsrcfiles since they're compiled using Babel, but not fortestfiles, in which it's not needed. In this scenario, the overrides syntax offered by ESLint (which would have suffered from the same limitation otherwise) comes in very handy indeed.slikts commentedon Jan 19, 2022
Colocating test, source and other types of files has been widely adopted as a pattern because it just makes sense for discoverability to group files by their concern and not their type. Test files in particular could benefit from looser types, which is why I've long wished that TypeScript would support overrides for colocated files and not just different directories. Lacking support for it means, for example, that people sometimes work around it with
ts-nocheckdirectives, losing even the benefits of loose typing.JacobMillner commentedon Feb 7, 2022
Another use case is when you have a very large project and would like to turn on a rule but can't just enable it globally. The best example would be enabling strict mode for single folders at a time, fixing the errors and then moving on to the next one over time.
slikts commentedon Feb 8, 2022
Jacob Millner (@JacobMillner) If it's for separate directories, you should be able to do what you mentioned by adding a nested
tsconfig.jsonthat extends from the root config and enables strict mode just for that directory and subdirectories.JacobMillner commentedon Feb 8, 2022
Reinis Ivanovs (@slikts) That works perfectly thank you!
steffenagger commentedon Feb 16, 2022
Donald Pipowitch (@donaldpipowitch): After seeing this suggestion I've tried out a modified structure, which we're currently using it in our latest project - just sharing our findings. Maybe it can unblock others 🤷 (but your initial suggestion would definitely be an improvement in my book).
I'm also interested in feedback on potential problems this setup might cause.
Click here to see full structure & findings
Into: We're using Vue3 (so adjust accordingly for other libs), with unit-tests running in Jest & e2e-tests + component-tests running in Cypress.
File patterns:
src/**/*.unit.tssrc/**/*.ct.tse2e/**/*.e2e.ts(not relevant in this context)tsconfig.jsoncontains our general setup, excluding all tests related - with the addition of"references":{ "compilerOptions": { "target": "esnext", "module": "esnext", "strict": true, "baseUrl": ".", "moduleResolution": "node", "esModuleInterop": true, "useDefineForClassFields": true, "resolveJsonModule": true, "sourceMap": true, "lib": ["esnext", "dom"], "paths": { "@/*": ["./src/*"] } }, "include": [ "src/**/*.ts", "src/**/*.vue", "src/shims-vue.d.ts" ], "exclude": [ "node_modules/cypress/types", "node_modules/@types/jest", "src/**/*.ct.ts", "src/**/*.e2e.ts", "src/**/*.unit.ts" ], "references": [ { "path": "./tsconfig.jest.json" }, { "path": "./tsconfig.cypress.json" } ] }tsconfig.cypress.json:{ "extends": "./tsconfig", "compilerOptions": { "composite": true, "types": ["cypress"] }, "include": [ "src/**/*.ts", "src/**/*.vue", "e2e/**/*.e2e.ts", "test-data/**/*.json", "src/shims-vue.d.ts" ], "exclude": [ "node_modules/@types/jest", "src/**/*.unit.ts" ] }tsconfig.jest.json::{ "extends": "./tsconfig", "compilerOptions": { "composite": true, "types": ["jest"] }, "include": [ "src/**/*.ts", "src/**/*.vue", "test-data/**/*.json", "src/shims-vue.d.ts" ], "exclude": [ "node_modules/cypress/types", "src/**/*.ct.ts", "src/**/*.e2e.ts", ] }Now this fixes most problems... BUT, some tools in our tool-chain (for type-checks) does not yet support "incremental mode", triggered by defining
"references"(as far as my understanding goes). This tooling is only used for actual production code (not tests), so I ended up writing a pre/post script for these tasks, essentially takingtsconfig.jsonremovingreferencesand temporarily saving it astsconfig.prod.json(which is specified for said tools) - as so:Pre script
temp-tsconfig.prod.json-create.js:Post script
temp-tsconfig.prod.json-cleanup.js:Donald Pipowitch (@donaldpipowitch): thank for the suggestion.
Though it's not exactly what I would call "elegant" (feels like a hack), it could be a solution - for now.
(it's a bit long, so collapsed 🤷)
redevill commentedon May 20, 2022
FWIW - Have also tried this "Project references / build mode" approach. However, found that "tsc -b" does not produce the same output as running tsc in the subproject directory. This was the case when one of the sub-projects tsconfig.json (extending the base tsconfig.base.json) was set to override the module to commonjs - from ESM. The output from tsc -b was ESM, and the tsc from the subproject directory came out commonjs.
AndreiSoroka commentedon Dec 2, 2022
Hi guys. I'm not sure but maybe it can help anybody
Problem
In my situation, my project is Vue + Storybook
But I don't want to keep the storybook like part of a project, and I move storybooks packages/config/settings into subfolder

where .stories.ts stay like part of project

So, I need to add rule only for *.storybook.ts files
But we don't have override rules for patterns for files, but can override
includeandexcludeI need to add to


includeall files"include": ["src/**/*"], because otherwise:but I have collision rules, so we should fix in the reverse situation
Solution
tsconfig.json
tsconfig.app.json
tsconfig.storybook.json
How it works


when we use any files but not
*.stories.tsthen we applytsconfig.app.jsonwhen the previous pattern is wrong, and we use any file (without exclude) then we apply
tsconfig.storybook.jsonI think it is difficult for more combinations.
What I expected
I expect from slow Microsoft Typescript team this feature:
or any other solution like https://eslint.org/docs/latest/user-guide/configuring/rules#using-configuration-files-1
P.s. the author of this issue gave a very cool example
we always need to make crutches, and we get answers after 3-4 years
mattidupre commentedon Jun 19, 2023
Andrei (@AndreiSoroka)'s answer ended up saving the day for me.
To clarify and simplify, a general
tsconfig-dist.jsonreference glob seems to need to be called first so it has to be listed last. For example, assume one of eachfile.test.ts,file.stories.ts,file.etc.ts,file.ts(dist) containing the following code that checks fornoImplicitAny:Assume one entry file
tsconfig.jsonpointing to onetsconfig-*.jsoneach matching their respective file above:The reason we have to
include: ['./**/*']and work offexclude: [...]is because our stories and tests will include general dist files. Iffile.stories.tstries to importfile.tsbut itstsconfig.jsonisinclude: ['./**/*.stories.*'], typescript will complain:So far this seems to work, but wouldn't one expect the order to be reversed? I agree with the general sentiment that this needs an explicit solution. Even something to the effect of an
includeImports: []to expand the scope of imports beyondinclude: []while allowing those files to be excluded on first match would do it.DiFuks commentedon Jun 20, 2024
Hello! I recently finished working on a TypeScript plugin that allows overriding config for files and folders. It's still pretty rough, but it works. I would be happy to get feedback :) https://github.com/DiFuks/ts-overrides-plugin