tsconfig.json should support multiple configurations
See original GitHub issueSearch Terms
multiple configurations, transitive closure
Suggestion
When using multiple packages in a monorepo, it is common to have a tsconfig.shared.json
that all packages extend in their individual tsconfig.json
. The current system makes it difficult to support various build configurations across packages (such as “dev” vs. “prod”).
Because a property named "config"
seems redundant in a file named tsconfig.json
, I’ll borrow the term select from Bazel and propose the following new option in a tsconfig.json
file for defining configurations:
// tsconfig.shared.json
{
// Via "select", we define two configurations: "universal" and "modern".
"select": {
"universal": {
"compilerOptions": {
"target": "es3",
"downlevelIteration": true,
},
},
"modern": {
"compilerOptions": {
"target": "es2019",
},
},
},
// These compilerOptions are common to both configurations.
"compilerOptions": {
"sourceMap": true,
"module": "esnext",
"moduleResolution": "node",
"lib": ["dom", "es5", "es2019"],
"strict": true,
},
}
To complement this, tsc
would have to support a new --select
flag:
# Assume tsconfig.json extends tsconfig.json and defines "include" and "references".
# This would build the modern configuration.
$ tsc --build --select modern -p tsconfig.json
Ideally, it would also be possible to parameterize paths such as outDir
so that you could also define this on the base compilerOptions
in tsconfig.shared.json
:
"outDir": "./dist/${select}/",
though I haven’t been able to get "outDir"
to do what I want in tsconfig.shared.json
in my own project, so there might be more work to do on that front.
Use Cases
I want to be able to have a shared tsconfig.json
that defines multiple configurations in one place that can be shared across multiple modules, as opposed to the current situation where we need O(M ⋅ C) tsconfig.json
files where M is the number of modules and C is the number of configurations. A detailed example is below.
Examples
I have a Yarn workspace where each package is under the modules/
folder, so my root package.json
contains the following:
{
"workspaces": [
"modules/*"
],
"scripts": {
"compile": "tsc --build modules/*"
}
}
I also have a shared.tsconfig.json
in the root of my Yarn workspace where I define the "compilerOptions"
that I want all packages in the workspace to use. (For example, I set "target": "es2019"
in shared.tsconfig.json
.) Each folder under modules/
contains its own tsconfig.json
file that contains the line:
"extends": "../../tsconfig.shared.json",
and there is also a "references"
property with the appropriate list of relative paths to other packages in the workspace.
Ideally, each such tsconfig.json
would contain only "extends"
and "references"
, but it seems I have to redeclare "compilerOptions.outDir"
and "include"
in each of these files even though the values are the same in each one (["src/**/*"]
and "./dist"
, respectively).
OK, so far, so good, but now I want to be able to build my entire Yarn workspace with a different set of compilerOptions
, specifically:
"downlevelIteration": true,
"target": "es3",
Ideally, I would be able to specify this in one place and leverage the "references"
I already had to define so I could compile one .ts
file or package and all of its transitive deps with these compiler options. (I happen to be using ts-loader
in Webpack, so my ultimate output is a single .js
file, which perhaps biases my expectations here.)
Unfortunately, there does not appear to be any clean way to do that. I could go through and define a tsconfig.es3.json
in each of my packages that looks like this:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"downlevelIteration": true,
"target": "es3",
},
}
though even if I did that, I’m not sure I could build all of my modules as ES3 with a one-liner as I did in my original package.json
file. Now I’m faced with the additional incidental complexity of:
- Maintaining O(M)
tsconfig.es3.json
files and ensuring all of them are in sync. - Introducing Lerna or some other tool to “walk my DAG” and run
tsc -b -p tsconfig.es3.json
or something like that.
With the --select
proposal, I could avoid the extra tsconfig.json
files and still build via a one-liner:
tsc --build --select universal modules/*
I would expect if any of the tsconfig.json
files under modules/*
did not define a "universal"
configuration, the build command would fail (or at least the strictness should be configurable).
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.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:12
- Comments:6 (2 by maintainers)
Top GitHub Comments
What do you guys think about supporting a
tsconfig.js
in addition totsconfig.json
? That way we would be free to build our own sharing / extending of configs through the full power of js. I personally prefer js-configs over json-config as they give me much more freedom and power (at the price of doing stupid mistakes of course).The team took a stance on that here, but I haven’t looked to see if anything’s changed about their stance since then.