How to consume a d.ts file's exported const types if the library's implementation is not integrated with the TS project?
If the module has an export as namespace myLib
then module already exports the library as a global object. So you can just use the library as:
let a:myLib.AnInterface;
let b = myLib.doThing1();
This is true as long as the file you are using the library in is not a module itself (ie it contains no import
and no export
statements).
export {} // module now
let a:myLib.AnInterface; // Types are still ok without the import
let b = myLib.doThing1(); // Expressions are not ok, ERR: 'myLib' refers to a UMD global, but the current file is a module. Consider adding an import instead.ts(2686)
You can add a property to Window
that is the same type as the type of the library using an import type (added in 2.9 in believe)
// myLibGlobal.d.ts
// must not be a module, must not contain import/ export
interface Window {
myLib: typeof import('./myLib') // lib name here
}
//usage.ts
export {} // module
let a:myLib.AnInterface; // Types are still ok without the import (if we have the export as namespace
let b = window.myLib.doThing1(); // acces through window ok now
Edit
Apparently the Typescript team has actually been working on something for this very issue. As you can read in this PR the next version of typescript will include a allowUmdGlobalAccess
flag. This flag will allow access to UMD module globals from modules. With this flag set to true, this code will be valid:
export {} // module now
let a:myLib.AnInterface; // Types are still ok without the import
let b = myLib.doThing1(); // ok, on [email protected]
This mean you can just access the module exports without the need for using window. This will work if the global export is compatible with the browser which I would expect it to be.
What are you dealing with
when a library assigns itself to a property of the window
That's called a UMD package. These are the ones consumed by adding a link inside a <script />
tag in your document, and they attach themselves to the global scope.
UMD packages don't have to be consumed this way — they can also be consumed as modules, using an import
(or require
) statement.
TypeScript supports both usages.
How should UMD packages be typed
declare namespace Foo {
export const bar: string;
export type MeaningOfLife = number;
}
export as namespace Foo;
export = Foo;
This definition tells TypeScript that:
- if the consuming file is a script, then there's a variable called
bar
in the global scope - if the consuming file is a module, then you can import
bar
using named imports or import the entire namespace using the wildcard (*
) import.
What's the difference between a script and a module?
A script would be a piece of JavaScript running inside a <script />
tag in your HTML document. It can be inlined or loaded from a file. It's how JavaScript has always been used in the browser.
A module is a JavaScript (TypeScript) file that has at least one import
or export
statement. They are part of the ECMAScript standard and are not supported everywhere. Usually, you create modules in your project and let a bundler like Webpack create a bundle for your application to use.
Consuming UMD packages
A script, variables, and types are used by accessing the global Foo
namespace (just like jQuery and $
):
const bar = Foo.bar;
const meaningOfLife: Foo.MeaningOfLife = 42;
A script, the type of bar
is imported using the import type syntax:
const baz: typeof import ('foo').bar = 'hello';
A module, the bar
variable is imported using a named import.
import { bar } from 'foo';
bar.toUpperCase();
A module, the entire package is imported as a namespace:
import * as foo from 'foo';
foo.bar.toUpperCase();
Global scope vs. window
If such a library is typed correctly, you as the consumer don't have to do anything to make it work. It will be available in your global scope automatically and no augmentation for Window
will be necessary.
However, if you'd like to attach the contents of your library to window
explicitly, you can also do that:
declare global {
interface Window {
Foo: typeof import('foo');
}
}
window.Foo.bar;