Issue
When I'm specifying a lib
option on my compilerOptions
:
"compilerOptions": {
"lib": ["esnext.full"]
}
VS Code gives a warning for esnext.full
. My understanding is that this should be valid, and should be roughly equivalent to specifying esnext
along with DOM
and DOM.Iterable
. How come VS Code doesn't recognize esnext.full
?
Solution
Why isn't
"esnext.full"
recognized as a valid"lib"
option in VS Code?
When searching for understanding, it's almost always more productive to seek the answer to questions when they're framed positively and skeptically (i.e. "What does exist?" / "Why is x valid?") — so, in this case: "Which values can be recognized?" Answering this question will provide knowledge about a restrictive scope, and a foundation for the rest of the answer.
The compilerOptions.lib
section in the TSConfig reference enumerates the built-in library and individual component identifiers which are valid for inclusion in a TSConfig (at the string array property compilerOptions.lib
).
I won't copy the entire list here — it's very large and subject to change.
The documentation itself recognizes the fact that the allowed values do change — by the inclusion of this footnote:
This list may be out of date, you can see the full list in the TypeScript source code.
Presumably, the link pointed to a valid resource at one point in time, but as I write this answer, navigating to the resource results in a 404 response (Not Found). Perhaps the repo has been restructured since the documentation was last updated — the default branch currently does not have a top-level lib
directory. However, by switching to a tag branch corresponding to a release (e.g. the v5.0.2
release from a few days ago), the top-level lib
directory can be viewed:
https://github.com/microsoft/TypeScript/tree/v5.0.2/lib
This is a directory of generated files (explained by the readme) — generated from data in the corresponding source directory src/lib
.
So, up to this point, the best source for determining valid identifiers is "consult the documentation" — which, as we've seen, is not guaranteed to be up-to-date/exhaustive — and that's not even close to an acceptable answer in my opinion.
The documentation has to be derived from something, right?
Digging deeper into the source repo, we can find a module in the compiler code: src/compiler/commandLineParser.ts
which contains a const array of tuples named libEntries
. Here's a partial snippet for reference:
const libEntries: [string, string][] = [
// ---snip---
["es2023", "lib.es2023.d.ts"],
["esnext", "lib.esnext.d.ts"],
// Host only
["dom", "lib.dom.d.ts"],
["dom.iterable", "lib.dom.iterable.d.ts"],
// ---snip---
];
This is used as the basis for valid library identifiers, and is used to create the following exports:
/**
* An array of supported "lib" reference file names used to determine the order for inclusion
* when referenced, as well as for spelling suggestions. This ensures the correct ordering for
* overload resolution when a type declared in one lib is extended by another.
*
* @internal
*/
export const libs = libEntries.map(entry => entry[0]);
/**
* A map of lib names to lib files. This map is used both for parsing the "lib" command line
* option as well as for resolving lib reference directives.
*
* @internal
*/
export const libMap = new Map(libEntries);
This is more satisfying. If you want an authoritative list of valid identifiers for use as lib
values, that's the place to view them. Be sure to consult the tag branch corresponding to the compiler version that you're using.
So now back to your question:
Why isn't
"esnext.full"
recognized as a valid"lib"
option in VS Code?
The answer is now obviously simple: it's not recognized because it's not a valid identifier in the list.
This leads to your next question:
Why is there a library file with the
.full
suffix?
As you observed in a comment, TypeScript includes some declaration files internally which cannot be used in the array of library names at compilerOptions.lib
in a TSConfig.
You linked to a JSON file in the source code which lists some strings in an array at a libs
key — these represent partial, relative path names to internal declaration files (see src/lib
). However, this is not a list of allowed library identifiers — it is simply a list for internal use by the compiler.
At the end of that list is a grouping of "default libraries" separated by a comment:
{
"libs": [
// ---snip---
// Default libraries
"es5.full",
"es2015.full",
"es2016.full",
"es2017.full",
"es2018.full",
"es2019.full",
"es2020.full",
"es2021.full",
"es2022.full",
"es2023.full",
"esnext.full"
],
// ---snip---
}
These values ending in the .full
suffix refer to the internal declaration files which the compiler uses by default (in absence of a compilerOptions.lib
array), according to the compilerOptions.target
value:
Changing
target
also changes the default value oflib
. You may “mix and match”target
andlib
settings as desired, but you could just settarget
for convenience.
Here's the utility function in the source which is used to derive the default library declaration file from the target
:
src/compiler/utilitiesPublic.ts
:
export function getDefaultLibFileName(options: CompilerOptions): string {
switch (getEmitScriptTarget(options)) {
case ScriptTarget.ESNext:
return "lib.esnext.full.d.ts";
// ---snip---
case ScriptTarget.ES2015:
return "lib.es6.d.ts"; // We don't use lib.es2015.full.d.ts due to breaking change.
default:
return "lib.d.ts";
}
}
Now, on to your final question:
What list of
lib
do I need to specify to get the equivalent of the default (everything pulled in by something likees2022.full
), but foresnext
ores2023
?
You listed two ECMAScript API collections — I'll use the first one you mentioned (esnext
) as the example. However, note that it's not a real spec — it refers to all "bleeding edge" features:
The special
ESNext
value refers to the highest version your version of TypeScript supports. This setting should be used with caution, since it doesn’t mean the same thing between different TypeScript versions and can make upgrades less predictable.
If you want to include the default libraries corresponding to esnext
(esnext.full
) in your compilation, here are two options for your TSConfig:
You can specify the libraries manually, along with your desired target:
First, in the TypeScript repo:
Navigate to the tag branch corresponding to the version of the compiler that you're using (in this example
v5.0.2
).Navigate to the top-level
lib
directory, and find the declaration file that corresponds to your target name and ends with.full.d.ts
(in this caselib.esnext.full.d.ts
).
After finding the corresponding declaration file, identify all of the libraries which are referenced by triple-slash directives. I'll inline the relevant content from the file linked above:
/// <reference no-default-lib="true"/> /// <reference lib="esnext" /> /// <reference lib="dom" /> /// <reference lib="webworker.importscripts" /> /// <reference lib="scripthost" /> /// <reference lib="dom.iterable" />
In your config:
Set
compilerOptions.target
to your desired target (for example, I'll use"es2020"
), andSet the
compilerOptions.lib
array to the libraries identified from the previous step above.
This looks like:
{ "compilerOptions": { "target": "es2020", "lib": [ "esnext", "dom", "webworker.importscripts", "scripthost", "dom.iterable", ], // ---snip--- }, // ---snip--- }
By the way, these compiler options values are not case-sensitive.
More conveniently (if you're ok with whatever the current default behavior might be), you can let the compiler use the defaults according to your compilation target by omitting the
compilerOptions.lib
array:Set
compilerOptions.target
to your target (in this case"esnext"
), andDon't define the
compilerOptions.lib
property at all.
This looks like:
{ "compilerOptions": { "target": "esnext", // ---snip--- }, // ---snip--- }
This will use the default libraries for your target (the internal
esnext.full
declaration file).
Finally, an observation: If you're targeting a normal browser environment, you likely don't want "webworker.importscripts"
and "scripthost"
which aren't part of that environment, but are included in the defaults. A more appropriate choice in that case might be ["esnext", "dom", "dom.iterable"]
.
This isn't an answer about compilation philosophy or best practice, but it's safer to compile to a single target environment for each compilation, and to restrict the included libraries to only what's available in that one target environment — this helps prevent using APIs that don't exist in the target environment and would cause runtime exceptions.
That was a bit of a journey, but I hope you now have greater confidence and better understanding of how library inclusion is handled during compilation.
Answered By - jsejcksn
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.