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
.fullsuffix?
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
targetalso changes the default value oflib. You may “mix and match”targetandlibsettings as desired, but you could just settargetfor 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
libdo I need to specify to get the equivalent of the default (everything pulled in by something likees2022.full), but foresnextores2023?
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
ESNextvalue 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
libdirectory, 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.targetto your desired target (for example, I'll use"es2020"), andSet the
compilerOptions.libarray 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.libarray:Set
compilerOptions.targetto your target (in this case"esnext"), andDon't define the
compilerOptions.libproperty at all.
This looks like:
{ "compilerOptions": { "target": "esnext", // ---snip--- }, // ---snip--- }This will use the default libraries for your target (the internal
esnext.fulldeclaration 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.