I am building an Electron app that imports a module called foo
that imports a module called bar
. I maintain all three projects.
Historically (but without a reason) all three projects have different values for target and lib in their tsconfig.json
.
Electron App tsconfig.json
:
"target": "es5",
"lib": ["es2017", "es2016", "es2015", "dom"]
node_modules/foo tsconfig.json
:
"target": "es2021",
"lib": ["es2019", "es2016", "es2015"]
node_modules/bar tsconfig.json
:
"target": "es2017",
"lib": ["es2015"]
Question: Given a TypeScript project, what effect do its target
and lib
values have on the transpilation of its files and on the transpilation of files of other projects which depends upon it? What kinds of problems could occur with mixed values between a dependent and its dependencies?
Here's a concrete example you can use for illustrative purposes: Can I set node_modules/bar
to target "es2022"
and still use it in the Electron project that targets "es6"
?
As an aside: Is it advantageous to list more entries in lib
?
The target
setting determines how JS language constructs (not standard JS APIs) get downleveled. I.e. syntactic sugar from newer JS language standards can be downleveled, but in general, the TS compiler will not / cannot downlevel usages of JS APIs which do not exist in older ECMA Script standards (ex. Promise.all
, Array.prototype.includes
).
The lib
setting determines what version of the standard JS APIs / builtin libraries (such as the DOM) should be expected to be available on the environment on which the code runs. I.e. JS APIs which are in the chosen standard will have their typings made available, and usages of those that are not available should trigger compiler warnings.
Note: you should not need to specify multiple ES libs at the same time. Just specifying the newest one you expect to be able to use should suffice, along with any desired subsections from newer versions (ex. es2015, es2017.String
). By listing multiple, you are telling the TS compiler to pull into scope typings for multiple JS API versions. I can't think of anything bad that would happen as a result of that, but neither can I think of anything good that could happen from it, and I'd rather bet my money that someone can think of something bad (as opposed to good) that would happen.
Each project/package is compiled from TS to JS on its own. When a project/package is compiled, there is no compilation of dependencies (which are compiled separately), and the TS compiler uses the settings in that project/package's tsconfig.json to compile it.
Here are some implications:
A project should not expect things to just work if it depends on another package which emitted JS for a newer target
than the one it chose
target
which it chose, but would have needed to be downleveled further for a dependent package which uses a lower target.A project should not expect things to just work if it depends on another package which uses newer lib
s (i.e. newer standard JS APIs) than those that it chose.
In both cases, the problem would technically be a problem for the dependent, and would be observed as the dependency not working as expected or erring on certain older environments which the dependent intended to support. There are two approaches to avoid/mitigate this situation from arising:
On the dependent side (the one that uses dependencies):
On the library author side:
target
and lib
, such as ES6 (ES2015), which is very well supported at the time of this writing. They do this to increase the chance that their library can be compatible with a dependent. You can thank them for that.I.e. Foo's usage of Bar is okay (both target
and lib
are higher in Foo than Bar), but the Electron App's usage of Foo is not.
I don't think the TS compiler will warn you if you make blunders when it comes to this, since after compilation, tsconfigs don't usually get distributed with packages. I.e. the TS compiler has no way to get that information about the package. But as already discussed, you should not. There's probably room for improvement in this area in the TS tooling and ecosystem, but for some reason, the current state of affairs is seen as normal/acceptable- Perhaps because many popular libraries make an effort to avoid language features and JS APIs which don't yet have very wide support.