microsoft / microsoft/TypeScript
Treat certain kinds of UMD initialization as a module in checkJS mode
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 117
Description
Bug Report
Here's plain JavaScript that works in Node, all Browsers for the last 3+ years, and all Bundlers (Vite, WebPack, etc) without the absolute minimum possible boilerplate, but tsc can't make heads or tails of it.
foo.js:
var Foo = ("object" === typeof module && exports) || {};
(function (window) {
"use strict";
let Crypto = window.crypto || require("node:crypto");
Foo.secret = new Uint8Array(16);
Crypto.getRandomValues(Foo.secret);
/**
* @param {String} name
*/
Foo.greet = function (name) {
console.log(`Hello, ${name}!`);
};
//...
console.log(Crypto.getRandomValues);
})(globalThis.window || {});
uses-foo.js:
"use strict";
let Foo = require("./foo.js");
// This SHOULD have an error (but it doesn't)
// (meaning that Foo became global, but shouldn't have)
/** @type {Foo} */
let fooFail = null;
console.log(fooFail);
// This should NOT have an error (but it does)
// - Foo should be module-exported,
// - Foo.greet should automatically typed by its explicit definition
/** @type {import('./foo').Foo} */
let greet = Foo.greet;
greet("AJ");
jsconfig.json:
{
"compilerOptions": {
"target": "es2022",
"moduleDetection": "force",
"module": "commonjs",
"moduleResolution": "node",
"typeRoots": ["./typings", "./node_modules/@types"],
"allowJs": true,
"checkJs": true,
"noEmit": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitAny": true,
"alwaysStrict": true,
"skipLibCheck": true
},
"include": ["*.js", "bin/**/*.js", "lib/**/*.js", "src/**/*.js"],
"exclude": ["node_modules"]
}
🔎 Search Terms
- umd
🕗 Version & Regression Information
Always
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about umd
⏯ Playground Link
I couldn't figure out how to put multiple files in the playground or how to set the jsconfig.json, but here's the link anyway:
💻 Code
- https://github.com/coolaj86/example-jswt-umd/tree/main/umd-ideal-but-broken
- https://github.com/coolaj86/example-jswt-umd/tree/main/umd-ugly-but-works
Here's the super hack workaround:
/**
* @typedef Foo2
* @prop {Greet2} greet
* @prop {Uint8Array} secret
*/
/**
* @callback Greet2
* @param {String} name
*/
/** @type {Foo2} */
//@ts-ignore
var Foo2 = ("object" === typeof module && exports) || {};
(function (window, Foo2) {
"use strict";
let Crypto = window.crypto || require("node:crypto");
Foo2.secret = new Uint8Array(16);
Crypto.getRandomValues(Foo2.secret);
/**
* @param {String} name
*/
Foo2.greet = function (name) {
console.log(`Hello, ${name}!`);
};
return Foo2;
})(globalThis.window || {}, Foo2);
if ("object" === typeof module) {
module.exports = Foo2;
}
Issues with the hacky workaround are:
- looks really ugly and confusing, but triggers all the right heuristics in however
tscis doing its thing
🙁 Actual behavior
Can't detect any of Foo's properties, exports Foo as a global rather that a script, doesn't export the exports at all.
🙂 Expected behavior
All type info should be module style (not global script style) and should know that Foo is module.exports and that anything assigned to Foo is exported.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the behavior in the umd-ideal-but-broken and umd-ugly-but-works directories, starting with foo.js, uses-foo.js, and jsconfig.json. Compare the failing example with the workaround, then verify that checkJs treats the supported UMD initialization as module-style, recognizes module.exports, and handles the Foo type annotations as expected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, typescript
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100