Skip to content

Commit 8b60e53

Browse files
committed
Some stuff
1 parent 8a3a826 commit 8b60e53

7 files changed

+798
-0
lines changed

DefinitelyTyped Migration.md

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
2+
3+
# Background
4+
5+
DefinitelyTyped currently hosts 1,622 type definitions.
6+
These were created from around 18,000 commits from 1,800 contributors over the course of 7,000 Pull Requests.
7+
8+
# Problems Today
9+
10+
We identify four prominent patterns of type definition files on DefinitelyTyped:
11+
12+
* *"declare module"*, where the sole top-level declaration in a file is `declare module "someName" { ... }`. Approximately 30% of files.
13+
* *"global"*, where only global declarations exist (e.g. no ambient external module declarations, no top-level `export` / `import` declarations). Approximately 40% of files.
14+
* *"mixed"*, containing both global declarations and an ambient external module declaration (`declare module "someName" { ... }`). Approximately 25% of files.
15+
* *"multiple module"*, where there are multiple declarations of the form `declare module "someName" { ... }`. Approximately 5% of files.
16+
17+
All of these forms have at least one declaration that ends up in the global namespace.
18+
This is a problem when multiple copies of these files are referenced in the same compilation; "Duplicate identifier" errors occur
19+
and users are confused about how to fix it.
20+
21+
# The New World
22+
23+
Two major new features in TypeScript will drastically simplify .d.ts authoring and distribution.
24+
25+
The first, UMD module definitions (https://github.com/Microsoft/TypeScript/issues/7125) will make
26+
the current UMD module declaration pattern obsolete.
27+
This will reduce global pollution and force declaration file authors to be more modular in their type layouts.
28+
29+
The second, library reference directives (https://github.com/Microsoft/TypeScript/issues/7156) will make
30+
it more predictable for users to reference typings files without having to memorize relative paths or filenames.
31+
32+
Unfortunately, both of these are new syntax that current versions of TypeScript won't understand.
33+
We will need to provide a transition path.
34+
35+
# The Path Forward
36+
37+
Going forward, we expect to see only two kinds of files:
38+
39+
* *"proper modules"*, which contain top-level `import` and `export` declarations. These are currently nonexistant on DefinitelyTyped.
40+
* *"global declarations"*, which do not contain `declare module "someName" {` or top-level `export` declarations.
41+
42+
Files written in the *declare module* format can be mechanically converted into *proper modules*.
43+
44+
Files written in the *global* format can be left as-is, though they should be reviewed to ensure they don't pollute the global scope more than needed.
45+
46+
*Mixed* files break down into two cases.
47+
When the file only declares one global, it can be rewritten to a UMD module without other impact.
48+
When the file declares more than one global, it will likely need to be substantially refactored to only expose one global.
49+
This will represent a breaking change to consumers of the .d.ts file.
50+
In the event this change is too broad, we may need to break the file out into separate global and module definition files.
51+
52+
Files written as *multiple module* need to be addressed on a case-by-case basis.
53+
They may be better suited to be left as-is (e.g. node.d.ts), or broken into separate definition folders.
54+
55+
# Transition Plan
56+
57+
Plan for transition:
58+
59+
* Create a new branch, `modern`
60+
* Auto-convert as many files as possible to the new format (current guess is that we can do this for 60% of folders)
61+
* Manually convert any popular libraries that weren't auto-converted
62+
* Start publishing compliant typings to NPM
63+
* Create a work item listing all remaining unpublished noncompliant typings
64+
65+
# Typings Sources
66+
67+
With the addition of TypeScript support for getting typings from NPM packages,
68+
we see three sources of type data going forward:
69+
70+
* **Bundled** typings are .d.ts files that are part of the NPM package of the library itself
71+
* **Community** typings are hosted on DefinitelyTyped and republished to the `@types/` namespace
72+
* **Third-party** typings are hosted DefinitelyTyped repos, but are still published to `@types/`
73+
74+
# UMD and Library Directives
75+
76+

Library Directives.md

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
2+
## Problems
3+
4+
`/// <reference path="..." />` comments (hereafter "reference directives") currently serve
5+
multiple purposes:
6+
* Including another definition file containing global declarations
7+
* Including another definition file containing ambient module declarations
8+
* Including another implementation file
9+
10+
The current mechanism has some common shortcomings:
11+
* Relative paths to a common typings folder can be come unwieldy
12+
* There's no mechanism to "override" a reference path
13+
* Reference directives only ever look in one place to find a file.
14+
15+
This has resulted in multiple issue reports of people trying to address this:
16+
* #5893 Multiple source roots - or something like classpath, like python_path, like include_path
17+
* #4615 Support environment variables in reference paths
18+
* #6482 Support a 'declarationPath' option
19+
20+
## New Use Case in Bundled Type Acquisition
21+
22+
An important scenario we need to address is the case where multiple typings
23+
files need to refer to the same *global* set of types or namespaces. If these
24+
libraries refer to multiple versions of the same global types, we have no
25+
existing mechanism for resolving the conflict
26+
27+
## Solution: Library include directive
28+
29+
### Syntax
30+
31+
The proposed syntax (:bike: :house: of course) would look like this
32+
```ts
33+
/// <reference library="jquery/jquery.d.ts" />
34+
```
35+
36+
### Behavior
37+
38+
This syntax means that the compiler should include the *first* file found
39+
when searching the following paths, where relative paths are resolved relative
40+
to the project root (either the location of `tsconfig.json`, or the common
41+
root of all input files):
42+
* `./typings/jquery/jquery.d.ts`
43+
* `./node_modules/jquery/jquery.d.ts`
44+
* `./node_modules/@types/jquery/jquery.d.ts`
45+
46+
This search order provides the following desirable behavior:
47+
* When needed, the user can resolve a version conflict by placing a definitive
48+
global version in the local `typings` folder
49+
* A bundled `.d.ts` file will be found automatically
50+
* A `types` package can fill in for libraries which don't have bundled definitions
51+
52+
We could conceivably allow for configuration of this search order in `tsconfig.json` if
53+
needed for complex scenarios.
54+
55+
### UMD
56+
57+
Additionally, when a UMD module definition is found, its global export declaration (if present)
58+
is added to the global scope.
59+
60+
## All-up World
61+
62+
Along with the rest of the typings acquisition story, we can now have a very coherent way to
63+
explain how file references should be managed in TypeScript programs:
64+
65+
* Modules which are part of your program are always imported using `import`
66+
* Modules which have definition files are always imported using `import`, and those `import`s should resolve to proper modules
67+
* Global definitions should always be located via `/// <reference library = ...` directives
68+
* File ordering for concatenated output is managed with `/// <reference path = ....` directives
69+
70+

Module Caveats.md

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
## Caveats
2+
3+
There are subtle differences in how CommonJS, RequireJS, SystemJS, and ES2015 (AKA ES6) module loaders
4+
expose the contents of modules.
5+
6+
To understand the most important distinctions, we'll start with an explanation of what an ES2015 module is.
7+
8+
### ES2015 modules
9+
10+
The definition of an ES2015 module is deceptively simple:
11+
An ES2015 module consists of a set of properties.
12+
These properties are created through the `export` keyword.
13+
14+
### `import` and `export` in ES2015
15+
16+
#### `export`
17+
18+
ES2015 supports a variety of `export` forms:
19+
```ts
20+
let a = 10;
21+
// Export the `a` variable under the name `a`
22+
export { a };
23+
// Export `a` under the name `b`
24+
export { a as b };
25+
26+
// Export the variable `c` under the name `c`
27+
export var c;
28+
29+
// Export the class `Foo` under the name `Foo`
30+
export class Foo { }
31+
32+
// Export the function `func` under the name `func`
33+
export function func() { }
34+
```
35+
36+
#### `export default`
37+
Additionally, most of these forms can accept the `default` modifier:
38+
```ts
39+
export default function() { }
40+
```
41+
42+
The semantics of the `default` keyword are very simple:
43+
A `default` export creates an export with the name `"default"`.
44+
45+
This bears repeating: the only effect in ES2015 of a `default` export
46+
is to create an export with the *name* "default".
47+
48+
### `import`
49+
50+
ES2015 also supports a variety of `import` forms:
51+
```ts
52+
// Create a local, `a`, from the `a` export of someMod
53+
import { a } from 'someMod';
54+
55+
// Import the entire module object of `someMod` as `obj`
56+
import * as obj from 'someMod';
57+
58+
// Create a local, `b`, from the `c` export of someMod
59+
import { c as b } from 'someMod';
60+
61+
// Create locals `Foo` and `func` from the corresponding exports of someMod
62+
import { Foo, func } from 'someMod'
63+
64+
// Create a local 'def' from the 'default' export of someMod
65+
import def from 'someMod';
66+
```
67+
68+
It's important to call out a distinction between these two lines:
69+
```ts
70+
import * as foo from 'someMod';
71+
import bar from 'someMod'
72+
```
73+
These look like they might do the same thing, but they don't.
74+
75+
The `foo` local contains the *module object* itself. The `bar` local contains the `default` export of that object.
76+
77+
In other words, `foo.default === bar` (or equivalently, `foo["default"] === bar`).
78+
79+
Again, the default export is *just a name*.
80+
The `import x from 'y'` form is syntactic sugar for retrieving the export named `default`.
81+
82+
### Exporting a top-level call signature
83+
84+
If you read the above text carefully, you'll notice there's no mechanism by which this would be legal:
85+
```ts
86+
import * as fn from 'someMod';
87+
fn(); // In ES2015, this cannot succeed
88+
```
89+
90+
This is because the module object, `fn`, is never based on a function.
91+
It is only a collection of properties.
92+
However, this is a common pattern in CommonJS and other legacy module systems.
93+
How is this handled today?
94+
95+
### Legacy Module Systems and Callable Modules
96+
97+
In CommonJS, for example, you might see this code:
98+
```ts
99+
var express = require('express');
100+
var x = express();
101+
```
102+
While this is legal in CommonJS and other module loaders, the `express` module is not
103+
compliant with ES2015 semantics.
104+
Only properties may be exported from an ES2015 module, not call signatures.
105+
106+
This presents a problem for module loaders because correctly emulating
107+
the ES2015 behavior would break many CommonJS modules.
108+
109+
This is solved through the *synthetic default export* behavior.
110+
111+
### Synthetic `default` exports
112+
113+
114+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
## The Problem
2+
3+
You have the classic JavaScript problem known as the *incorrect `this` context*.
4+
The [`this` keyword in JavaScript][1] behaves differently than in does in other languages like C# and Java.
5+
6+
<!-- Call out the exact line here -->
7+
8+
### How `this` works
9+
10+
The `this` keyword, in a function, is determined as follows:
11+
* If the function was created through a call to `.bind`, the `this` value is the argument provided to `bind`
12+
* If the function was *invoked* through a method call, e.g. `expr.func(args)`, then `this` is `expr`
13+
* Otherwise
14+
* If the code is in [*strict mode*][2], `this` is `undefined`
15+
* Otherwise, `this` is `window` (in a browser)
16+
17+
Let's look at how this works in practice:
18+
19+
class Foo {
20+
value = 10;
21+
doSomething() {
22+
// Prints 'undefined', not '10'
23+
console.log(this.value);
24+
}
25+
}
26+
let f = new Foo();
27+
window.setTimeout(f.doSomething, 100);
28+
29+
This code will print `undefined` (or, in strict mode, throw an exception).
30+
This is because we ended up in the last branch of the decision tree above.
31+
The `doSomething` function was invoked, the function wasn't a result of a `bind` call, and it wasn't invoked in a method syntax position.
32+
33+
We can't see the code for `setTimeout` to see what its invocation looks like, but we don't need to.
34+
Something to realize is that all `doSomething` methods point to *the same function object*.
35+
In other words:
36+
37+
let f1 = new Foo();
38+
let f2 = new Foo();
39+
// 'true'
40+
console.log(f1.doSomething === f2.doSomething);
41+
42+
We know that `setTimeout` can only see the function we passed it, so when it invokes that function,
43+
there's no way for it to know which `this` to provide.
44+
The `this` context has been lost due to our *referencing* the method without *invoking* it.
45+
46+
### The Red Flag
47+
48+
Once you know about `this` problems, they're easy to spot:
49+
50+
class Foo {
51+
value = 10;
52+
method1() {
53+
doSomething(this.method2); // DANGER, method reference without invocation
54+
}
55+
method2() {
56+
console.log(this.value);
57+
}
58+
}
59+
60+
## The Solution
61+
62+
You have a few options here, each with its own trade-offs.
63+
The best option depends on how often the method in question is invoked from differing call sites.
64+
65+
### Arrow Function in Class Definition
66+
67+
Instead of using the normal method syntax, use an [arrow function][3] to initialize a per-instance member.
68+
69+
class DemonstrateScopingProblems {
70+
private status = "blah";
71+
72+
public run = () => {
73+
// OK
74+
console.log(this.status);
75+
}
76+
}
77+
let d = new DemonstrateScopingProblems();
78+
window.setTimeout(d.run); // OK
79+
80+
* Good/bad: This creates an additional closure per method per instance of your class. If this method is usually only used in regular method calls, this is overkill. However, if it's used a lot in callback positions, it's more efficient for the class instance to capture the `this` context instead of each call site creating a new closure upon invoke.
81+
* Good: Impossible for external callers to forget to handle `this` context
82+
* Good: Typesafe in TypeScript
83+
* Good: No extra work if the function has parameters
84+
* Bad: Derived classes can't call base class methods written this way using `super.`
85+
* Bad: The exact semantics of which methods are "pre-bound" and which aren't create an additional non-typesafe contract between your class and its consumers.
86+
87+
### Function Expression at Reference Site
88+
89+
Shown here with some dummy parameters for explanatory reasons:
90+
91+
class DemonstrateScopingProblems {
92+
private status = "blah";
93+
94+
public something() {
95+
console.log(this.status);
96+
}
97+
98+
public run(x: any, y: any) {
99+
// OK
100+
console.log(this.status + ': ' + x + ',' + y);
101+
}
102+
}
103+
let d = new DemonstrateScopingProblems();
104+
// With parameters
105+
someCallback((n, m) => d.run(n, m));
106+
// Without parameters
107+
window.setTimeout(() => d.something(), 100);
108+
109+
* Good/bad: Opposite memory/performance trade-off compared to the first method
110+
* Good: In TypeScript, this has 100% type safety
111+
* Good: Works in ECMAScript 3
112+
* Good: You only have to type the instance name once
113+
* Bad: You'll have to type the parameters twice
114+
* Bad: Doesn't easily work with variadic parameters
115+
116+
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
117+
[2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
118+
[3]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

0 commit comments

Comments
 (0)