Pesquisa de site

Como obter o autocompletar do VS Code/TypeScript sem importar a biblioteca


Se um módulo (por exemplo, moment.js, knockout ou big.js) for incluído com uma tag <script> por exemplo

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

que definem uma propriedade global (por exemplo, moment, ko, Big, etc), como alguém pode acessar/declarar os tipos em window (ou global) no Typescript.

Por exemplo

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

Como alguém pode definir o tipo ambiente desses globais sem incluir toda a biblioteca moment.js? O objetivo é ter referências globais devidamente tipadas para serem usadas pelo VS Code, e tsc, ts-loader ou babel-typescript.

No caso do moment, os tipos são expostos em node_modules/moment/moment.d.ts, mas para outras bibliotecas (por exemplo, knockout ou big.js) eles estão em @types/[module]/index.d.ts.

Parece que isso seria bastante comum, mas eu não vi uma boa referência funcional de como realizar isso.

Aqui está o 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"
  ]
}

Aqui está um 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'),
  }
}

e aqui está como o consumo deve funcionar:

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

que se parece com isto no VS Code (com o sublinhado vermelho indicando falhas):

Então o knockout funciona porque @types/knockout/index.d.ts tem:

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

enquanto eu declarei de forma semelhante um Big global em interface Window.

Infelizmente, Sentry e moment (neste exemplo) não parecem funcionar, e não está claro por que ou o que alguém teria que fazer para corrigir isso.

Resposta #1

Você tem a ideia certa, você precisa usar um tipo import para obter os tipos em uma importação, você precisa declarar variáveis no global. O problema é que você está declarando as propriedades em Window. Embora qualquer chave atribuída ao Window se torne uma variável global, o TypeScript não reflete isso. Você pode declarar as variáveis diretamente no escopo 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

Resposta #2

A questão não é terrivelmente específica para um tipo de plataforma, mas eu vou te contar como eu faço o moment funcionar no Angular.

Primeiro, você não deveria estar carregando a partir de uma URL de script. Você deveria executar npm install moment para colocá-lo na sua pasta node_modules. Isso garantirá que ele seja incluído na versão minificada da sua aplicação. Você não precisa, mas isso é fortemente recomendado.

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

Independentemente do exposto acima, uma vez que você tenha o script vinculado em seu tsconfig (ou angular.json), o uso é tão simples quanto a seguinte linha no topo do arquivo:

import * as moment from 'moment';

Eu costumo usar essa sintaxe para a maioria das bibliotecas que têm um objeto, por exemplo, shortid, linq e lodash.

Outras bibliotecas funcionam melhor com declarações declare var. Por exemplo, eu descobri que o jQuery funciona melhor dessa maneira.

declare var $

Preenchimento automático

Se você está procurando por autocompletar, você precisa ter um arquivo .d.ts carregado para a biblioteca. O pacote npm para moment já vem com um, mas algumas bibliotecas precisam que outro pacote npm seja instalado para isso (por exemplo, @types/lodash, @types/jquery).

O Autocompletar do Visual Studio depende de arquivos .d.ts para identificar tipos. A documentação para configurar esse recurso está localizada aqui, e pode ser possível configurá-lo (veja a nota próxima ao final).