Brian M. Hunt
Brian M. Hunt

Reputation: 83808

How to get VS Code / typescript autocomplete without importing the library

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):

failing types

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

Answers (2)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

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

theMayer
theMayer

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

Related Questions