|
1 | 1 | # [arethetypeswrong.github.io](https://arethetypeswrong.github.io)
|
2 | 2 |
|
3 |
| -This project attempts to analyze npm package contents for issues with their TypeScript types, particularly ESM-related module resolution issues. Currently, the following kinds of problems can be detected in the `node10`, `node16`, and `bundler` module resolution modes: |
4 |
| - |
5 |
| -- **The package doesn’t ship types.** In the future, the tool may pull typings from DefinitelyTyped and check for agreement ([dts-critic](https://github.com/microsoft/DefinitelyTyped-tools/tree/master/packages/dts-critic) is prior art). |
6 |
| -- **Resolution failed.** Occurs when an import of the package (or a package subpath defined in its package.json `exports`) fails to resolve completely. This will result in a TypeScript compiler error, and may indicate that the runtime corresponding to the module resolution mode tested might fail to resolve it too. |
7 |
| -- **Untyped resolution.** Occurs when TypeScript can resolve a JavaScript file, but no type declaration file. This is a TypeScript compiler error under `noImplicitAny`. |
8 |
| -- **Types are CJS, but implementation is ESM.** In the `node16` resolution mode, TypeScript detects whether Node itself will assume a file is a CommonJS or ECMAScript module. If the types resolved are detected to be CJS, but the JavaScript resolved is detected to be ESM, this problem is raised. It is often caused by a dependency’s package.json doing something like: |
9 |
| - ```json |
10 |
| - { |
11 |
| - "exports": { |
12 |
| - "types": "./index.d.ts", |
13 |
| - "import": "./index.mjs", |
14 |
| - "require": "./index.js" |
15 |
| - } |
16 |
| - } |
17 |
| - ``` |
18 |
| - Because there is no `"type": "module"` setting here, the `.js` and `.d.ts` file will always be interpreted as a CommonJS module. But if the _importing_ file is an ES module, the runtime will resolve to the `.mjs` file, which is unambiguously ESM. In this case, the module kind of the types misrepresents the runtime-resolved module. This tends to present the most issues for users when default exports are used. In the future, this tool may detect whether an `export default` might make this problem more severe and give a full explanation of why. In the meantime, you can read the explanation in [this issue](https://github.com/microsoft/TypeScript/issues/50058#issuecomment-1404411380). The simple fix for the example above would be to add an `index.d.mts` file dedicated to typing the `.mjs` module, and remove the `"types"` condition. |
19 |
| -- **Types are ESM, but implementation is CJS.** The reverse of the above, but with worse consequences. Because a CommonJS module cannot access an ES module without (async) dynamic import, this kind of mismatch means that TypeScript will _falsely_ belive that a CJS module is unable to import the package without dynamic import. This is more rare, but can happen with a similar situation as the above, but where the package.json has `"type": "module"`, and the importing file is a CJS module. |
20 |
| -- **Syntax is incompatible with detected module kind.** In Node, as well as in some bundlers, whether a file should be interpreted as CJS or ESM is a pure function of the file extension and the nearest ancestor package.json `type`. If these signal that the file is ESM, `module` and `require` will be `undefined`. If they signal that the file is CJS, `import` and `export` statements will be syntax errors. This problem is raised when syntax incompatible with the detected module kind is used. |
21 |
| -- **Package (or subpath) is ESM-only.** This occurs when a CommonJS importing module in `node16` resolves to an ES module (and not falsely so, as above, as far as we can tell). This is a TypeScript compiler error and will be a runtime error in Node. It’s not necessarily a defect of the package; it likely just means that the author decided to only publish ESM, leaving their CommonJS consumers without a good option. |
22 |
| -- **Resolved through a fallback condition.** In Node, when resolution of an import path hits conditional `"exports"` in a package.json, it tries to resolve with the first matching condition. If resolution fails with that condition, the process is over and the import is not resolved. TypeScript’s algorithm instead continues through the list of conditions and attempts to resolve with any other condition that matches, until resolution succeeds or the no more conditions match. This is [a TypeScript bug](https://github.com/microsoft/TypeScript/issues/50762), so its behavior should not be relied upon—even if the bug never gets fixed, results that arise from it are likely to be innacurate since the resolution process diverges from Node. |
23 |
| -- **CJS module uses a default export.** CommonJS files can indicate to bundlers that a default import should resolve to its `module.exports.default` instead of its `module.exports` by setting `module.exports.__esModule` to `true`. Node does not respect the `__esModule` marker, though, so default imports of the file in Node will have to add an additional `.default` property access in order to get to the file’s intended default export: |
24 |
| - ```ts |
25 |
| - // main.mts: |
26 |
| - import doSomething from "dependency"; |
27 |
| - doSomething(); // doesn't work |
28 |
| - doSomething.default(); // works |
29 |
| - ``` |
30 |
| - This problem is reported when a CJS module appears to use `module.exports.default`, has an `__esModule` marker, and its types use `export default`. It’s important to notice that in this case, the types and the implementation agree. Adding a `.default` is necessary, both to run in Node and to satisfy the TypeScript compiler. There are no inconsistencies here, but the pattern is discouraged since the code needed to use the import in a bundler and the code needed in Node are mutually incompatible. Instead, the library is encouraged to use `module.exports` instead of, or in addition to, `module.exports.default`. So no, the types aren’t wrong, but the package likely isn’t functioning in Node like they intended. |
31 |
| -- **Types incorrectly use a default export.** This happens when a CJS file uses `module.exports =` but its types use `export default`. The correct type declaration for `module.exports = ...` is `export = ...`. This mismatch doesn’t usually present a problem in bundlers, but for Node, TypeScript will falsely think a default import from an ESM file needs an additional `.default`, as in the example from the previous problem. |
32 |
| - |
33 |
| -## Future work |
34 |
| - |
35 |
| -The first things on my roadmap: |
36 |
| - |
37 |
| -- More thorough explanations of problems |
38 |
| -- Support for DefinitelyTyped analysis |
39 |
| -- Official TypeScript module documentation to link to |
40 |
| - |
41 |
| -Some other ideas: |
42 |
| - |
43 |
| -- Use unpkg or something so it uses less data |
44 |
| -- Generate a downloadable reproduction of a problem |
45 |
| -- Analyze traces and failed lookup locations to suggest fixes |
| 3 | +This project attempts to analyze npm package contents for issues with their TypeScript types, particularly ESM-related module resolution issues. Packages can be explored via the [website](https://arethetypeswrong.github.io) or [CLI](./packages/cli). The following kinds of problems can be detected in the `node10`, `node16`, and `bundler` module resolution modes: |
| 4 | + |
| 5 | +* [💀 Resolution failed](./docs/problems/NoResolution.md) |
| 6 | +* [❌ No types](./docs/problems/UntypedResolution.md) |
| 7 | +* [🎭 Masquerading as CJS](./docs/problems/FalseCJS.md) |
| 8 | +* [👺 Masquerading as ESM](./docs/problems/FalseESM.md) |
| 9 | +* [⚠️ ESM (dynamic import only)](./docs/problems/CJSResolvesToESM.md) |
| 10 | +* [🐛 Used fallback condition](./docs/problems/FallbackCondition.md) |
| 11 | +* [🤨 CJS default export](./docs/problems/CJSOnlyExportsDefault.md) |
| 12 | +* [❗️ Incorrect default export](./docs/problems/FalseExportDefault.md) |
| 13 | +* [🚭 Unexpected module syntax](./docs/problems/UnexpectedModuleSyntax.md) |
| 14 | +* [🥴 Internal resolution error](./docs/problems/InternalResolutionError.md) |
46 | 15 |
|
47 | 16 | ## Contributing
|
48 | 17 |
|
49 |
| -- Understanding that the site is rather incomplete, issue reports are ok. |
50 |
| -- I’m open to someone with design chops adding some styles, but I want to keep it simple. Reach out in an issue. |
| 18 | +Contributions are welcome! Take a look at the open issues or read about [how to contribute to open source](https://opensource.guide). |
0 commit comments