Skip to content

Proposal for merging array or object options in tsconfig fileΒ #57486

Open
@sheetalkamat

Description

@sheetalkamat

πŸ” Search Terms

"extends", "merge", "compilerOptions", "paths", "outdir", "include", "exclude", "typeRoots", "tsconfig", "ability to merge tsconfig"

βœ… Viability Checklist

⭐ 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/base.tsconfig.json
{
    "compilerOptions": {
        "paths": {
            "@libs/*": ["libs/*"],
            "@project1/*": ["project1/*"]
        },
        "typeRoots": ["node_modules/@types"],
    }
}
// @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

  1. What do you want to use this for?
  2. What shortcomings exist with current approaches?
  3. What workarounds are you using in the meantime?

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions