microsoft / microsoft/TypeScript

Treat certain kinds of UMD initialization as a module in checkJS mode

Aperta
#52,377 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Awaiting More Feedback Suggestion
Lingua principale
Go
Stelle
111k
Fork
14.3k
Merge medio
1g 19h
PR unite (30g)
117

Descrizione

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:

https://www.typescriptlang.org/play?declaration=false&target=9&jsx=0&module=1&filetype=js#code/G4QwTgBAYg9jEF4IAoBEMBGArApgYwBdVEEkCBPABxxgDMIBbGAEwFcAbHCAMm4hwAelGGAIBnAJQQAPtIgBvAL4BuAFDJarAHaEAljC0oA7rq3MYRqfNUQIqVmK5iCYXYVRqbETgQgBhMCoCeCQTMwsAOjxAymCZOTAcAEdWXUS0LRYcAC5ooJhUCU9bWBgIx2icXyQtHCMIAFVTAgAOAEEwMBByZABGADYirwD8iIBzKoAlEHCGADUQdlYcMWRS8vxEgiGvAHoAKn2vCH2IAAFKcBAGBQBlF1MxxQgta5xj-d2vdbHEqsQIJodAR9IZkK8GDgrMcIHgDGIYJwIuwYGNkAADAASOHYKIANBAACTyCE4RQAQnRQ1sKlUe12EUZXjhWgRSJRaJGsTKEwI01mCyWKyGigkyDGKIwiwAKgALXRiCJhcz1WQKUXKIA

💻 Code

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 tsc is 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.

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Riproduci il comportamento nelle directory umd-ideal-but-broken e umd-ugly-but-works, iniziando da foo.js, uses-foo.js e jsconfig.json. Confronta l’esempio che non funziona con la soluzione alternativa, quindi verifica che checkJs tratti l’inizializzazione UMD supportata come un’inizializzazione in stile modulo, riconosca module.exports e gestisca le annotazioni di tipo Foo come previsto.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
javascript, typescript
Ambito
compilers
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
35/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.