0

I have a JavaScript project, which can be reduced to this:

console.log(depA());
console.log(depB());

I am pulling in the dependencies by using the plain old script tag:

<html>
  <head>
    <script src="pkg/depA/index.js"></script>
    <script src="pkg/depB/index.js"></script>
    <script src="src/index.js"></script>
  </head>
</html>

The packages I am using are JavaScript packages, both are like this:

function depX() { // Where X is A or B
  return 'depX';
}

Both of these packages have their own index.d.ts typings alongside theirindex.js`.

For package A this is straightforward:

declare function depA(): string;

For package B, which can also be used as a module, the typings are also a module file:

declare function depB(): string;
export default depB;

Like I said, this is a JavaScript project and I am using TypeScript for type checking:

{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "noEmit": true,
    "types": [
      "../pkg/depA",
      "../pkg/depB",
    ]
  }
}

I a referencing the package typings using compilerOptions.types, because this being a plain JavaScript project, I am not using NPM or anything to install the packages or the typings, and since this is JavaScript code, there are no import statements or requires (I am not using ESM and there is no build process for CJS), so TypeScript cannot be aware of the packages and know to pull in their types in any other way but being told explicitly using compilerOptions.types.

The problem is, typings for the package B communicate it is a module file, so the depB function is declared on the module scope, not the window scope. depA and depB are only demonstrations, there are real world packages with which I am facing this, so I have no option of removing the export default and making the typings a non-module file, but I can create a pull request and add support for making the typings file both a window-global function and a module other consumers whole use modules can import.

My question then is, how can depB typings be amended so they remain a module file (and no existing consumers are broken), but they also communicate the depB function exists on the window scope, so TypeScript knows it's okay to just call it in-place, standalone, no import, no nothing?

At runtime, that is the case, so the typings are the problem, they just don't communicate this for TypeScript to know (I think), so I ask, how can they be changed to do so?

I've made a GitHub repository which demonstrates the problem, there is an associated public Azure Pipeline which runs the TypeScript type check which shows depA is accessible with no problem, but depB is invisible at window as it is communicated as only being a module.

1 Answer 1

1

The pattern you are describing is often used to define UMD packages. Their contents can be imported or — if there is no module bundler — accessed from the global scope.

TypeScript handbook explains what their definitions should look like. Your case is defined in the module-function.d.ts template, which can be reduced to:

depB.d.ts

declare function depB(): string;

export as namespace depB;
export = depB;

Including the above in your project did the job for me, and no upstream changes were required.

See the pull request.

Sign up to request clarification or add additional context in comments.

2 Comments

Is it possible to reconcile this with export default? It already exists in the upstream library I am using and am looking to contribute support for global-scope access to, but the above solution when applied to depB/index.d.ts says An export assignment cannot be used in a module with other exported elements. I guess this is a legit limitation, so I will use a local file like you have in your branch (thanks!) if not possible, but I am curious nonetheless.
The error is correct — you cannot have export = together with any ESM export. You can, however, make TypeScript treat this export as a default one by enabling the allowSyntheticDefaultImports flag in your tsconfig.json. In this scenario, the above typings can go upstream (pkg/depB/index.d.ts) and no local file is required. See the pull request.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.