Cum să obții completarea automată în VS Code/TypeScript fără a importa biblioteca


Dacă un modul (de exemplu moment.js, knockout sau big.js) este inclus cu un tag <script>, de exemplu

<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.js">
</script>

care definesc o proprietate globală (de exemplu moment, ko, Big, etc.), cum poate cineva să acceseze/declare tipurile pe window (sau global) în Typescript.

De exemplu

const x = moment()
const t = ko.observable()
const b = new Big()

Cum poate cineva să seteze tipul ambient al acestor globale fără a include întreaga bibliotecă moment.js? Obiectivul este de a avea referințe globale corect tipizate care să poată fi folosite de VS Code, și tsc, ts-loader sau babel-typescript.

În cazul moment, tipurile sunt expuse la node_modules/moment/moment.d.ts, dar pentru alte biblioteci (de exemplu knockout sau big.js) acestea se află la @types/[module]/index.d.ts.

Se pare că acest lucru ar fi destul de comun, dar nu am văzut o referință bună și funcțională despre cum să realizezi asta.

Iată tsconfig-ul:

{
    "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"
  ]
}

Iată un 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'),
  }
}

și iată cum ar trebui să funcționeze consumul:

const v = new Big(1)
const m = moment()
const s = global.Sentry()
const o = ko.observable()

care arată astfel în VS Code (cu sublinierea roșie indicând eșecurile):

Deci knockout funcționează pentru că @types/knockout/index.d.ts conține:

declare var ko: KnockoutStatic;
declare module "knockout" {
    export = ko;
}

în timp ce am declarat în mod similar un Big global pe interface Window.

Din păcate, Sentry și moment (în acest exemplu) nu par să funcționeze și nu este clar de ce sau ce ar trebui să facă cineva pentru a rezolva acest lucru.

Răspuns #1

Ai idee bună, trebuie să folosești un tip import pentru a obține tipurile într-un import, trebuie să declari variabilele în global. Problema este că declari proprietățile pe Window. Deși orice cheie atribuită Window devine o variabilă globală, TypeScript nu reflectă acest lucru. Poți declara variabilele direct în domeniul global.

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

Răspunsul #2

Întrebarea nu este extrem de specifică pentru un singur tip de platformă, dar îți voi spune cum fac să funcționeze moment în Angular.

În primul rând, nu ar trebui să încarci de la un URL de script. Ar trebui să rulezi npm install moment pentru a-l plasa în folderul tău node_modules. Aceasta va asigura că este inclus în versiunea minificată a aplicației tale. Nu trebuie neapărat să o faci, dar este foarte recomandat.

"scripts": [
          "node_modules/jquery/dist/jquery.slim.min.js",
          "node_modules/moment/moment.js",
           ...
        ]

Indiferent de cele de mai sus, odată ce ai scriptul legat în tsconfig (sau angular.json), utilizarea este la fel de simplă ca următorul rând din partea de sus a fișierului:

import * as moment from 'moment';

Tind să folosesc această sintaxă pentru majoritatea bibliotecilor care au un obiect, de exemplu shortid, linq și lodash.

Alte biblioteci funcționează mai bine cu declarațiile declare var. De exemplu, am observat că jQuery funcționează mai bine în acest mod.

declare var $

Completare automată

Dacă cauți completare automată, trebuie să ai un fișier .d.ts încărcat pentru bibliotecă. Pachetul npm pentru moment vine cu unul, dar pentru unele biblioteci este necesar să fie instalat un alt pachet npm pentru aceasta (de exemplu, @types/lodash, @types/jquery).

Funcția de completare automată în Visual Studio se bazează pe fișierele .d.ts pentru a identifica tipurile. Documentația pentru configurarea acestei funcționalități se află aici și este posibil să fie configurată (consultați nota de aproape de partea de jos).