Typescript: forçar importação

Criado em 4 set. 2014  ·  31Comentários  ·  Fonte: microsoft/TypeScript

Tenho muitos cenários em que faria isso:

import myExternalModule = require("./myExternalModule");
// not using myExternalModule here

Eu não uso myExternalModule em meu código, mas ainda quero que ele seja incluído usando requirejs. Eu só preciso que ele esteja lá.
Se pudesse haver uma palavra-chave forceimport seria muito legal!

Question

Comentários muito úteis

então porque não apenas adicionar

import * as React from "react"; // get the types
import "react";  // just for side effect

na saída, a última importação: import "react" não será omitida.

Todos 31 comentários

Deve ser possível forçar a emissão escrevendo

var myExternalModule = require("./myExternalModule");

mas isso não reclamará se "./myExternalModule" for um caminho incorreto.

A abordagem correta é apresentar uma referência fictícia que não tenha efeitos colaterais:

import myExternalModule = require("./myExternalModule"); 
myExternalModule;

Embora seja um hack, é muito improvável que uma nova sintaxe seja introduzida para esse caso de uso bastante restrito. Veja os objetivos do

Se o TypeScript verificasse o caminho em var myExternalModule = require("./myExternalModule"); , isso permitiria aos usuários forçar o emit _and _ obter a verificação em tempo de compilação, sem ter que recorrer ao hack.

A abordagem var não importa os tipos do módulo.

Além disso, você pode usar o atributo amd-dependency, se necessário.

Com que propósito você está importando coisas que não são usadas?

Por exemplo, em um aplicativo angular, tenho algumas diretivas em outros arquivos. Preciso importar essas diretivas ou minha marcação html não funcionará. No entanto, não os uso em JavaScript.
Normalmente no angular, a maioria dos módulos são desacoplados e não têm dependências entre si, mas precisam ser importados para que todo o aplicativo funcione.

: +1:
Isso está me confundindo.
Por que o compilador TypeScript eliminou isso?
o módulo externo não é um módulo não instanciado.
require tem um efeito colateral.
A cláusula de importação parece ser compatível com os requisitos de AMD e CommonJS. mas não é.

Este problema também é irritante quando require () - ing módulos que realmente não exportam nada, mas ao invés disso corrigem a funcionalidade em objetos globais, como es5-shim , es6-shim e muitos outros.

Isso está me confundindo.
Por que o compilador TypeScript eliminou isso?

É uma otimização porque às vezes queremos importar apenas as informações de tipo de um módulo:

import foo = require('foo');

function bar(paramOne: foo.ParamOne, paramTwo: foo.ParamTwo){}

não é apenas uma otimização. Se você estiver usando um módulo importado apenas para tipos (ou seja, não usado em nenhuma posição de valor), não podemos emitir a lógica necessária para ele, pois o módulo pode ser um módulo apenas de tipo ambiente que não existe em tempo de execução. A emissão de uma importação de módulo significaria um erro de tempo de execução ao tentar carregar um módulo não existente.

Você pode usar a sinalização amd-dependency para isso:

/// <amd-dependency path="foo" />

import x = require('foo');

Em vez de <amd-dependency> que parece meio que um hack, teria sido interessante usar <reference name="..."> IMO.

@danquirk É this poderia ter um tipo # 229.

A importação é usada para carregar dependências antes que o código atual seja carregado e não pode ser referenciado diretamente. Dependências angulares são um exemplo, enquanto plug-ins jQuery são outro. Qualquer biblioteca que estende um objeto base e não é referenciada diretamente é afetada pelo "recurso". Ao decidir não incluir arbitrariamente uma importação com base na análise estática local, você está impondo um padrão de compilador de estilo C sobre a intenção explícita do desenvolvedor que escreveu a instrução de importação. A expectativa da importação de gravação é que ela seja incluída como uma dependência e disponível para o escopo local do módulo. Qualquer outra ação é um efeito colateral da expectativa natural de escolher escrever import dentro de um módulo.

É muito possível que, nesse caso, o compilador TypeScript faça menos e faça mais.

A próxima versão do TypeScript oferecerá suporte a módulos de estilo ES6 (a alteração está atualmente no master). então, se tudo o que você deseja é declarar dependência, você pode usar:

import "myLib";

O compilador não omitirá essa importação.

O comportamento inconsistente existente será abordado ou permanecerá como algo divertido para as pessoas descobrirem? Deve ser adicionado à documentação que é um caso em que import não adiciona o arquivo como uma dependência? Ao construir módulos TypeScript AMD, ///<reference... agora será considerado um erro?

A maneira import funciona atualmente é um projeto ruim. O design ruim cria ambigüidade. Se o comportamento adicional não estiver claramente definido, é mais do que provável que seja descoberto, embora seja um efeito colateral. Daí o OP, eu e outros comentários e bugs relacionados a ele neste fórum.

Existe um conjunto de práticas que já existe ao usar o gerenciamento de dependência de estilo Comum ou Exigir. A implementação de import mostra que ela foi ignorada.

Com relação à documentação, consulte a seção de especificações seção 11.2.6 :

Uma declaração de importação externa é representada no JavaScript gerado como uma variável inicializada por uma chamada para a função 'require' fornecida pelo host do sistema do módulo. Uma declaração de variável e chamada 'require' são emitidas para um módulo importado específico apenas se o módulo importado, ou um alias local (seção 10.3) que faz referência ao módulo importado, _ for referenciado como uma PrimaryExpression em algum lugar no corpo do módulo importado. Se um módulo importado é referenciado apenas como um ModuleName ou TypeQueryExpression, nada é emitted_.

/// referências não são iguais às importações. /// referências trazem declarações adicionais em seu escopo global, por exemplo, APIs dom. é uma declaração ao compilador para confiar na existência dessas entidades e não tem impacto no código gerado.

Por outro lado, as importações precisam ser emitidas como instruções de requerimento. no caso de você estar usando apenas uma importação em uma posição de tipo, a intenção não é clara, ou você deseja apenas os tipos (que são construções de tempo de design que não existem em tempo de execução), e neste caso você deseja que a importação seja excluída , e se não for, você provavelmente importará um módulo apenas de tipo inexistente. Ou você quer os efeitos colaterais também. Para o último, import "mod"; sintaxe parece ser uma sintaxe mais clara para declarar a intenção.

@mhegazy Não quero abrir um novo fascículo, mas este é um
Considere o código abaixo, quando compilado para js, a referência React é removida porque não aparece no código e o código irá falhar, pois ReactRouter depende dela. A correção ... realmente idiota está abaixo do código. Existe alguma maneira mais inteligente de fazer isso? Obrigado.

import * as React from "react"; var temp = React.DOM;
....

// mf("route.schedules", "") // this is to register a translation
anonymousRoutes.route("/schedules", {
  name: "schedules",
  subscriptions: function() {
    this.register("schedules", subscriptions.subscribe("schedules"));
  },
  action: () => {
    ReactLayout.render(ClearLayout, {content: <Book />});
  }
});

FIX;) ​​- EHM

import * as React from "react"; var temp = React.DOM;
...

o código falhará porque o ReactRouter depende dele.

ele precisa do var "React" ou apenas alguns efeitos colaterais da importação?

Ele precisa do efeito colateral da importação. O var foi adicionado apenas para não remover a importação no código transpilado.

então porque não apenas adicionar

import * as React from "react"; // get the types
import "react";  // just for side effect

na saída, a última importação: import "react" não será omitida.

Infelizmente isso não funciona e eu ainda consigo:

ReferenceError: React is not defined
    at action [as _action] (schedule_router.jsx:15)
    at Route.callAction (kadira_flow-router.js?f961d732ed2b89a53d490af5979b899800aa1a5d:2306)
    at kadira_flow-router.js?f961d732ed2b89a53d490af5979b899800aa1a5d:2025
    at Object.Tracker.nonreactive (tracker.js:560)
    at kadira_flow-router.js?f961d732ed2b89a53d490af5979b899800aa1a5d:2016
    at Tracker.Computation._compute (tracker.js:294)
    at Tracker.Computation._recompute (tracker.js:313)
    at Object.Tracker._runFlush (tracker.js:452)
    at Object.Tracker.flush (tracker.js:412)
    at Router._invalidateTracker (kadira_flow-router.js?f961d732ed2b89a53d490af5979b899800aa1a5d:2065)

Acho que o motivo aqui são as "dependências fracas" no Meteor, que dependem da _callee_ para fornecer todas as referências necessárias para elas.

Comecei a aprender datilografia recentemente para o Angular 2. Isso me surpreendeu várias vezes, como tenho certeza que o fará para outros recém-chegados. Eu entendo que isso é uma otimização, mas na verdade é muito confuso. Se eu digitar algo em meu arquivo ts, espero que produza, produza ou de algum tipo, não simplesmente ignore. Eu entendo que posso forçar a produção da instrução require, mas isso parece um pouco estranho. Esta parece ser uma causa em que eu, como usuário, devo ser capaz de tomar minhas próprias decisões sem que o compilador decida o que é melhor.

O TypeScript sobrepõe a declaração de tipo sobre os valores, de modo que a mesma sintaxe de importação fornece tipos, valores ou ambos. isso torna muito conveniente e intuitivo trabalhar com o módulo; se você precisa de um tipo de outro módulo, apenas importe-o ao importar uma variável, ou apenas importe a classe, e use-a tanto como um tipo quanto como um construtor com new.

O outro lado disso, você pode fazer importações para módulos que não existem, pois são apenas contêineres do tipo. se o compilador emitiu referências a esses módulos, ele falhará em tempo de execução. a solução que temos é detectar _como_ a importação foi usada e _todas_ as referências a ela são usadas como tipos, então não é necessário em tempo de execução e eliminado.

obviamente, isso pode ser confuso se você também precisar dos efeitos colaterais de um módulo. mas o contrário também é confuso se uma importação para um módulo não existente foi emitida, deixando um lado ter que perder a conveniência de empacotar tipos e valores juntos na mesma sintaxe.

Uma solução alternativa:
Se estiver usando a resolução tradicional ( moduleResolution não está definido como node ), uma declaração de importação contendo ! em seu identificador nunca será evitada. Eu acredito que isso tem algo a ver com a nossa tentativa de suportar melhor algum comportamento systemjs , mas pode ser usado para forçar uma importação. Se você _estiver_ usando systemjs, requirejs ou qualquer carregador que permita o remapeamento de identificadores de módulo, você pode encerrar sua importação forçada em !js ou uma marca semelhante e mapeá-la com seu carregador de módulo. O TS emitirá a importação na íntegra (e não tentará verificar o tipo ou resolvê-la) e seu carregador pode ser ensinado a entender a importação.

Parece que a equipe já está tendo que criar um código de caso especial para esses tipos de importações, como em checker.ts:

// If we're compiling under --jsx react, the symbol 'React' should
// be marked as 'used' so we don't incorrectly elide its import. And if there
// is no 'React' symbol in scope, we should issue an error.
if (compilerOptions.jsx === JsxEmit.React) {
    let reactSym = resolveName(node.tagName, "React", SymbolFlags.Value, Diagnostics.Cannot_find_name_0, "React");
    if (reactSym) {
        getSymbolLinks(reactSym).referenced = true;
    }
}

Como @mhegazy aponta, não se trata apenas de otimização, mas sim de evitar a necessidade de uma importação sem definição de tempo de execução (por exemplo, Interface), o que causaria um erro de tempo de execução.

Eu questiono o que vai acontecer com mais frequência. Você pode detectar facilmente se a importação é um * .d.ts, de forma que esses sejam facilmente eliminados.

No restante dos casos, parece uma coisa bastante trivial para o compilador detectar que o módulo importado não teria saída em tempo de execução e apenas eliminasse essas importações.

De uma perspectiva DRY , esta sugestão me deixou desejando ter um transpiler para o seu transpiler para escrever este código duplicado para mim.

import * as React from "react"; // get the types
import "react";  // just for side effect

A meu ver, deve ser a escolha do arquivo _importado_ se ele precisa ser carregado em tempo de execução (porque sabe que tem efeitos colaterais). Isso realmente não deve ser decidido pelo arquivo _loading_ (ou seja, o arquivo com a instrução de importação), uma vez que não é seu negócio saber detalhes de implementação de outros arquivos / módulos.

Pelo menos isso funcionaria bem do ponto de vista do angularjs, onde um arquivo que define diretivas e serviços "sabe" que deve ser carregado em tempo de execução se referenciado, portanto, o compilador não deve excluí-lo. Algum tipo de dica em um arquivo para o compilador de texto digitado "não elidir" consertaria esse IMO. Mas provavelmente estou esquecendo algum caso de uso aqui.

-JM

xmodule.ts:

console.log('ive been imported');
export class X {
}
import {X} from "xmodule"; // get the types
import "xmodule";  // just for side effect

Se xmodule tiver instruções, por exemplo, a instrução console.log, por definição não é apenas uma pilha de declarações. Não deve ser responsabilidade do módulo de importação fazer declarações que usem xmodule para que xmodule seja considerado mais do que apenas uma série de declarações de tipo.

@weswigham

Você mencionou o uso de um estrondo para forçar sua importação. Isso é o preferido para o meu cenário. Como faço para usá-lo? Tentei todos os seguintes:

!import {Service} from './service';
import! {Service} from './service';
import {!Service} from './service';
import {Service!} from './service';
import {Service} from '!./service';
import {Service} from './service!';
import {Service} from !'./service';
import {Service} from './service'!;

Estava na string - mas acho que o comportamento foi removido recentemente, quando os mapeamentos de caminho foram introduzidos.

Estou usando a biblioteca, como react, para importar node wrapper - h (in react é react () se não me engano), então eu importo assim:

import { h, Component } from "preact";

Então, é claro, não tenho h no tempo de execução. Porque por que estaria lá? Além disso, usando com jsx preserve, o TypeScript realmente não trata de h, mas babel sabe.

Então, o que resta, usando referência como h; depois da importação, sim?

Para h (ou qualquer outra fábrica jsx personalizada), use --jsxFactory para permitir que o compilador saiba que esta é a fábrica que você está usando em tempo de execução. por exemplo, --jsxFactory h .

Isso não requer typescript@next no momento, deve estar disponível no TypeScript 2.1.2.

@mhegazy Para projetos que têm dois tipos de consumidor JSX (como react e snabbdom), não é aceitável.

Estou trabalhando em um projeto que usa o react para renderizar a interface do usuário da web e um dom virtual personalizado implementado por nós mesmos para renderizar objetos webgl. E isso atinge o caso, já que PRECISAMOS de dois tipos diferentes de anotação @jsx para o mesmo projeto.

Agora sou forçado a exportar nosso próprio h como uma variável global ... É feio.

Incluir-me na lista de desenvolvedores que precisam / querem uma importação de um liner que faça isso. Duas linhas de código-fonte para conseguir isso é o que estamos tentando eliminar, especialmente quando queremos importar muitos módulos em uma linha. Acho que o que me pareceu melhor até agora é uma palavra-chave / sintaxe para forçar a importação. Obrigado novamente pessoal!

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

uber5001 picture uber5001  ·  3Comentários

weswigham picture weswigham  ·  3Comentários

kyasbal-1994 picture kyasbal-1994  ·  3Comentários

dlaberge picture dlaberge  ·  3Comentários

wmaurer picture wmaurer  ·  3Comentários