Reputation: 83808
If a module (e.g. moment.js
, knockout
, or big.js
) is included with a <script>
tag e.g.
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.js">
</script>
that define a global property (e.g. moment
, ko
, Big
, etc), how can one access/declare the types on window
(or global
) in Typescript.
For example
const x = moment()
const t = ko.observable()
const b = new Big()
How can one set the ambient type of these globals without including the entire moment.js
library? The objective is to have properly typed global references to be used by VS Code, and tsc
, ts-loader
, or babel-typescript
.
In the case of moment
, the types are exposed at node_modules/moment/moment.d.ts
, but for other libraries (e.g. knockout
or big.js
) they're at @types/[module]/index.d.ts
.
It feels like this would be quite common, but I haven't seen a good, working reference of how to accomplish this.
Here is the tsconfig:
{
"compilerOptions": {
"target": "ESNext",
"moduleResolution": "node",
"allowJs": true,
"noEmit": true,
"strict": false,
"isolatedModules": false,
"esModuleInterop": true,
"noResolve": false,
"baseUrl": ".",
"paths": {
"*": [
"*",
"js.packages/*"
]
},
"jsx": "preserve",
"outDir": "dist/"
},
"include": [
"js.packages/**/*.ts",
"js.packages/**/*.tsx",
"js.packages/@types/lib.d.ts",
],
"files": [
"services/app/src/entry.js"
],
"exclude": [
"node_modules"
]
}
Here's a lib.d.ts
declare global {
type Bigable = Big | string | number
interface Window {
Big: typeof import('big.js'),
moment: typeof import('moment'),
Sentry: typeof import('@sentry/browser'),
}
}
and here's how consumption should work:
const v = new Big(1)
const m = moment()
const s = global.Sentry()
const o = ko.observable()
which looks like this in VS Code (with the red underline indicating failures):
So knockout works because @types/knockout/index.d.ts
has:
declare var ko: KnockoutStatic;
declare module "knockout" {
export = ko;
}
whereas I've similarly declared a global Big
on interface Window
.
Unfortunately Sentry
and moment
(in this example) do not seem to work, and it's unclear why or what one might have to do to fix this.
Upvotes: 1
Views: 3486
Reputation: 249606
You have the right idea, you need to use an import
type to get the types in an import, you need to declare variables in global
. The problem is that you are declaring the properties on Window
. While any key assigned to Window
becomes a global variable typescript does not reflect this. You can declare the variables directly in the global scope.
declare global {
export var moment: typeof import('moment');
export var Sentry: typeof import('@sentry/browser');
}
const m = moment()
Sentry.init({}) // Sentry() does not seem like it should work according to docs
Upvotes: 8
Reputation: 16167
The question is not terribly specific to one type of platform, but I'll tell you how I get moment
working in Angular.
First, you shouldn't be loading from a script URL. You should run npm install moment
to put it in your node_modules
folder. This will ensure it gets packed up into the minified version of your application. You don't have to, but this is strongly recommended.
"scripts": [
"node_modules/jquery/dist/jquery.slim.min.js",
"node_modules/moment/moment.js",
...
]
Regardless of the above, once you have the script linked in your tsconfig
(or angular.json
), usage is as simple as the following line at the top of the file:
import * as moment from 'moment';
I tend to use this syntax for most libraries which have an object, e.g. shortid
, linq
, and lodash
.
Other libraries work better with declare var
statements. For example, I've found jQuery to work better that way.
declare var $
Autocomplete
If you're looking for autocomplete, you need to have a .d.ts
file loaded for the library. The npm package for moment
comes with one, but some libraries need to have another npm package installed for this (e.g. @types/lodash
, @types/jquery
).
Visual Studio Autocomplete relies upon .d.ts
files to identify types. The documentation for configuring this feature is located here, and it may be possible to configure it (see the note near the bottom).
Upvotes: 0