Description
Types in es5.d.ts Should Match the ECMAScript 5.1 Standard
As a TypeScript user, I expect that an environment configured with "lib": ["es5"]
should only resolve types specified in the ECMAScript 5.1 Language Specifications. Any further type declarations necessary should be optionally imported or specified by the TypeScript user. Indeed, this appears to be the suggested usage, as pointed out by @mhegazy in #1995 (emphasis mine):
you can always augment your program with additional types that you know would exist on your target system. for instance, if you know you are always running on an engine that supports promises, just add declarations for Promise in your program. The idea is that the lib file reflects the baseline of engines supporting the target of the compilation (ES5 v.s. ES6)
However, including only the "es5" lib will also bring in the following non-ES5 types (and their support types):
- Decorators - Introduced when es5.d.ts was core.d.ts (2015/03/17).
PromiseLike
- This was pulled into core.d.ts from es6.d.ts on 2015/05/05.ArrayBuffer
andTypedArray
s - This was merged into core.d.ts from es6.d.ts and extension.d.ts on 2015/08/15.Promise<T>
- This was pulled into es5.d.ts from es2015.promise.d.ts on 2016/02/13.- Several others not listed...
Including these types breaks expectations in properly initialized environments and has the potential to lead to confusion, if not bugs.
Why Are These Types In es5.d.ts to Begin With?
In short: to support the DOM API declarations.
From what I've been able to glean from the history, the fact that the above type declarations exist in es5.d.ts is effectively a kludge to more simply support the constantly-evolving DOM API libraries. From the comments on #14053 (which brought Promise<T>
into es5.d.ts):
Move the declaration of the Promise interface to es5.d.ts (and subsequently lib.d.ts). This allows for DOM APIs that return Promise to be typed accurately.
Indeed, supporting DOM APIs appears to explain much of the reason for including the previously listed types in es5.d.ts. Again, in #16077 we have the following comment:
The sole purpose of including Promise and Typed Arrays to prevent errors from lib.dom.d.ts where APIs e.g. Fetch and Web Audio use them.
[It should be noted that in the above case, the discussion revolves around whether or not to move the types further back in time (to ES3).]
Moving "Support" Types Out of es5.d.ts
So the types are defined in the ES libs to support the DOM APIs (which suffer from granularity issues). Here are some options for how they might be removed from the es5.d.ts file:
1. Create a Middleman Library
It appears that ES5 is used as the default ECMAScript library for TypeScript - at some point es5.d.ts was even called core.d.ts. But a core.d.ts could still exist: it could import es5.d.ts and then some extra "support" types (e.g. those outlined above). That file could then be specified in the lib.d.ts library source map (as well as others).
2. Specify Necessary Features Directly in the Library Source Map
Specific features that DOM APIs require might be included from their respective "future" libraries. An example for promises might look like:
// ...
{ target: "lib.dom.d.ts", sources: ["header.d.ts", "dom.generated.d.ts"].concat("es2015.promise.d.ts") },
// ...
{ target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources, "es2015.promise.d.ts") },
// ...
provided Promise
type interfaces were moved back into es2015.promise.d.ts (unless I'm missing something important).
3. Declare Necessary Interfaces Inline
Declare required, but possibly new/future types as empty interfaces inline in the libraries that require it. In at least one Node Type Declaration library I've encountered, they did just this:
interface MapConstructor { }
interface WeakMapConstructor { }
interface SetConstructor { }
interface WeakSetConstructor { }
What's more, this even seems to be a suggested approach (emphasis mine):
you can always augment your program with additional types that you know would exist on your target system. for instance, if you know you are always running on an engine that supports promises, just add declarations for Promise in your program. The idea is that the lib file reflects the baseline of engines supporting the target of the compilation (ES5 v.s. ES6)
Wrap-up: Being Explicit About Type Definition
Instituting a change like this would not only clean up the ECMAScript type declaration file(s), it would explicitly document why those types were included with certain libraries. Presently, a user trying to figure out why UInt8Array
is a suggested type when they've only imported type declarations for ECMAScript 5 ("lib": ["es5"]
) will find that it's confusingly included in the es5.d.ts file, sans any documentation for its mysterious inclusion.
Further, it would decouple DOM support from ECMAScript support, enabling users to more explicitly configure their environments without surprising side-effects.