Description
π Search Terms
"extends", "merge", "compilerOptions", "paths", "outdir", "include", "exclude", "typeRoots", "tsconfig", "ability to merge tsconfig"
β Viability Checklist
- 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, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
β Suggestion
Subset of #56436
Today when you extend a config the options written in the final config override the options from the base config. Here is sample of what happens today:
// @fileName: /temp/test/project1/tsconfig.json
{
"extends": "../base.tsconfig.json",
"compilerOptions": {
"paths": {
"@environment/*": ["env/*"]
},
"typeRoots": ["typings"]
}
}
Here is what it would mean that project1 tsconfig was written as:
// @fileName: /temp/test/project1/computed.tsconfig.json
{
"compilerOptions": {
"paths": {
"@environment/*": ["env/*"]
},
"typeRoots": ["./typings"],
}
}
Over years users have asked us for ability to create a final config that adds to base config instead of overriding. This is extremely useful for paths
from compiler options.
This would allow to users to write a base config that contains common options that shouldn't have to be copied in all projects and yet allowing projects to specify there additional specific options.
For example in above sample, one would expect a way to write config such that final paths
property is:
// @fileName: /temp/test/project1/computed.tsconfig.json
{
"compilerOptions": {
"paths": {
"@libs/*": ["../libs/*"],
"@project1/*": ["../project1/*"],
"@environment/*": ["env/*"]
},
"typeRoots": ["../node_modules/@types", "./typings"],
}
}
Here are different proposals that are being considered:
Per Property Specific options
1. merge
fields
Add merge
as root level config property that specifies options that need to merged instead of completely overwritten in the final config.
From the above example writing the project1
config as:
// @fileName: /temp/test/project1/tsconfig.json
{
"extends": "../base.tsconfig.json",
"compilerOptions": {
"paths": {
"@environment/*": ["env/*"]
},
"typeRoots": ["typings"]
},
"merge": ["paths", "typeRoots"]
}
2. Inline merge
options:
// @fileName: /temp/test/project1/tsconfig.json
{
"extends": "../base.tsconfig.json",
"compilerOptions": {
"merge": {
// All the options that need to be merged
"paths": {
"@environment/*": ["env/*"]
},
"typeRoots": ["typings"]
},
},
}
3. merge:optionName
in instead of just optionsName
// @fileName: /temp/test/project1/tsconfig.json
{
"extends": "../base.tsconfig.json",
"compilerOptions": {
"merge:paths": {
"@environment/*": ["env/*"]
},
"merge:typeRoots": ["typings"]
},
}
4. Root level merge:OptionsBagName
to merge.
From the above example writing the project1
config as:
// @fileName: /temp/test/project1/tsconfig.json
{
"extends": "../base.tsconfig.json",
"merge:compilerOptions": {
"paths": {
"@environment/*": ["env/*"]
},
"typeRoots": ["typings"]
},
}
5. merge-optionsName: true
in the options
// @fileName: /temp/test/project1/tsconfig.json
{
"extends": "../base.tsconfig.json",
"compilerOptions": {
"merge:paths": true,
"merge:typeRoots": true,
"paths": {
"@environment/*": ["env/*"]
},
"typeRoots": ["typings"]
},
}
6. Simple placeholder like ${base}: true
or <merge>: true
// @fileName: /temp/test/project1/tsconfig.json
{
"extends": "../base.tsconfig.json",
"compilerOptions": {
"paths": {
"${base}": true,
"@environment/*": ["env/*"]
},
"typeRoots": ["${base}", "typings"]
},
}
7. Detailed template substitution
This should allow you to control the order esp when extending multiple configs
From the above example writing the project1
config as:
// @fileName: /temp/test/project1/tsconfig.json
{
"extends": "../base.tsconfig.json",
"compilerOptions": {
"paths": {
"${0}": true,
"@environment/*": ["env/*"]
},
"typeRoots": ["typings", "${0}"]
},
}
Global merge options
8. Add root level merge: true
that merges all arrays and objects options:
From the above example writing the project1
config as:
// @fileName: /temp/test/project1/tsconfig.json
{
"extends": "../base.tsconfig.json",
"merge": true,
"compilerOptions": {
"paths": {
"@environment/*": ["env/*"]
},
"typeRoots": ["typings"]
},
}
Here is a chart of all options that are list
or object
type:
Option Category | Option | Type | Order Dependency | Eligible for Merge | |
---|---|---|---|---|---|
CompilerOptions | paths | object | mayBe not | #44589, #20110 , For merging paths, we set pathsBasePath for resolving relative path, what happens to that when paths are merged |
|
CompilerOptions | lib | list | no | #20110 | |
CompilerOptions | rootDirs | list | yes? | ||
CompilerOptions | typeRoots | list | yes | Now that we resolve types only from typeRoots, this seems to be important? | |
CompilerOptions | types | list | no? | global types to include | |
CompilerOptions | moduleSuffixes | list | yes | ||
CompilerOptions | customConditions | list | no | ||
CompilerOptions LS | plugins | list | mayBe | ||
WatchOptions | excludeFiles | list | no | ||
WatchOptions | excludeDirectories | list | no | ||
TypeAcquisition | include | list | no | ||
TypeAcquisition | exclude | list | no | ||
Root | files | list | mayBe | #20110 | |
Root | include | list | mayBe | #20110 | |
Root | exclude | list | mayBe | #20110 | |
Root | references | object | no | Transitive, vs listing only direct parent? #30608, #27098 |
π Motivating Example
A way to specify say project specific "paths" mapping without having to rewrite all the mappings from the base config.
π» Use Cases
- What do you want to use this for?
- What shortcomings exist with current approaches?
- What workarounds are you using in the meantime?