Typescript: forzar la importación

Creado en 4 sept. 2014  ·  31Comentarios  ·  Fuente: microsoft/TypeScript

Tengo muchos escenarios en los que haría esto:

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

No uso myExternalModule en mi código, pero aún así quiero que se incluya usando requirejs. Solo necesito que esté ahí.
Si pudiera haber una palabra clave forceimport , ¡sería genial!

Question

Comentario más útil

entonces, ¿por qué no agregar

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

en la salida, la última importación: import "react" no se eliminará.

Todos 31 comentarios

Debería ser posible forzar la emisión escribiendo

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

pero esto no se quejará si "./myExternalModule" es una ruta incorrecta.

El enfoque correcto es introducir una referencia ficticia que no tenga efectos secundarios:

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

Aunque esto es un truco, es muy poco probable que se introduzca una nueva sintaxis para este caso de uso bastante limitado. Vea los objetivos de diseño.

Si TypeScript verificara la ruta en var myExternalModule = require("./myExternalModule"); , eso permitiría a los usuarios forzar la emisión _y _ obtener la verificación en tiempo de compilación, sin tener que recurrir al truco.

Sin embargo, el enfoque var no importa los tipos del módulo.

Además, puede utilizar el atributo amd-dependency si es necesario.

¿Con qué propósito está importando cosas que no se utilizan?

Por ejemplo, en una aplicación angular tengo algunas directivas en otros archivos. Necesito importar esas directivas o, de lo contrario, mi marcado html no funcionará. Sin embargo, no los uso en JavaScript.
Por lo general, en angular, la mayoría de los módulos están desacoplados y no tienen dependencias entre sí, pero deben importarse para que funcione toda la aplicación.

: +1:
Me está confundiendo.
¿Por qué el compilador TypeScript lo elimina?
El módulo externo no es un módulo no instanciado.
require tiene un efecto secundario.
La cláusula de importación parece ser compatible con los requisitos de AMD y CommonJS. pero no lo es.

Este problema también es molesto cuando se requieren () - módulos que en realidad no exportan nada, sino que modifican la funcionalidad en objetos globales, como es5-shim , es6-shim y muchos otros.

Me está confundiendo.
¿Por qué el compilador TypeScript lo elimina?

Es una optimización porque a veces queremos importar solo la información de tipo de un módulo:

import foo = require('foo');

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

no es solo una optimización. Si está utilizando un módulo importado solo para tipos (es decir, no se utiliza en ninguna posición de valor), no podemos emitir la lógica requerida para él, ya que el módulo puede ser un módulo de tipo ambiente únicamente que no existe en tiempo de ejecución. Emitir una importación de módulo significaría un error en tiempo de ejecución al intentar cargar un módulo no existente.

Puede usar la bandera amd-dependency para esto:

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

import x = require('foo');

En lugar de <amd-dependency> que parece un truco, habría sido interesante usar <reference name="..."> IMO.

@danquirk Es arbitrario y peligroso que el motor TypeScript asuma la responsabilidad de optimizar las declaraciones de importación. Hay muchos patrones que TypeScript aún no captura de JavaScript. Un ejemplo es cómo no aborda que this podría tener un tipo # 229.

La importación se utiliza para cargar dependencias antes de que se cargue el código actual y es posible que no se haga referencia a él directamente. Las dependencias angulares son un ejemplo, mientras que los complementos de jQuery son otro. Cualquier biblioteca que amplíe un objeto base y no se haga referencia directamente a ella se verá afectada por "característica". Al decidir no incluir arbitrariamente una importación basada en el análisis estático local, está imponiendo un patrón de compilador de estilo C sobre la intención explícita del desarrollador que escribió la declaración de importación. La expectativa de la importación de escritura es que se incluirá como una dependencia y estará disponible para el ámbito local del módulo. Cualquier otra acción es un efecto secundario de la expectativa natural de elegir escribir import dentro de un módulo.

Es muy posible que en este caso el compilador de TypeScript pueda hacer menos y lograr más.

La próxima versión de TypeScript admitirá los módulos de estilo ES6 (el cambio está actualmente en el maestro). entonces, si todo lo que quieres es declarar dependencia, puedes usar:

import "myLib";

El compilador no omitirá esta importación.

¿Se abordará el comportamiento inconsistente existente o se mantendrá como algo divertido de descubrir para la gente? ¿Debería agregarse a la documentación que hay un caso en el que import no agrega el archivo como una dependencia? Al crear módulos AMD de TypeScript, ¿ ///<reference... ahora se considerará un error?

La forma en que import funciona actualmente es un mal diseño. El mal diseño crea ambigüedad. Si el comportamiento adicional no está claramente definido, lo más probable es que se descubra a través de su efecto secundario. Por lo tanto, el OP, yo mismo y otros comentarios y errores relacionados con él en este foro.

Existe un cuerpo de práctica existente que ya existe cuando se usa la administración de dependencias de estilo común o requerido. La implementación de import muestra que fue ignorada.

Con respecto a la documentación, consulte la sección de especificaciones sección 11.2.6 :

Una declaración de importación externa se representa en el JavaScript generado como una variable inicializada por una llamada a la función 'require' proporcionada por el host del sistema del módulo. Se emite una declaración de variable y una llamada 'require' para un módulo importado en particular solo si el módulo importado, o un alias local (sección 10.3) que hace referencia al módulo importado, _ se hace referencia como PrimaryExpression en algún lugar del cuerpo del módulo de importación. Si se hace referencia a un módulo importado solo como ModuleName o TypeQueryExpression, no se emite nada_.

/// las referencias no son lo mismo que las importaciones. /// referencias trae declaraciones adicionales a su alcance global, por ejemplo, API dom. es una declaración para que el compilador confíe en la existencia de estas entidades y no tiene impacto en el código generado.

Por otro lado, las importaciones deben emitirse como declaraciones requeridas. en el caso de que solo esté utilizando una importación en una posición de tipo, la intención no está clara, o desea solo los tipos (que son construcciones de tiempo de diseño que no existen en tiempo de ejecución) y, en este caso, desea que la importación se elide , y si no es así, posiblemente estaría importando un módulo de solo tipo no existente. O también quiere los efectos secundarios. Para este último, la sintaxis import "mod"; parece ser una sintaxis más clara para declarar la intención.

@mhegazy No quiero abrir una nueva edición, pero esto es un pequeño problema en el mundo React.
Considere el código a continuación, cuando se compila en js, la referencia React se elimina ya que no aparece en el código y el código fallará ya que ReactRouter depende de ello. La solución ... realmente tonta está debajo del código. ¿Hay alguna forma más inteligente de hacerlo? Gracias.

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;
...

el código fallará ya que ReactRouter depende de él.

¿Necesita la var "React" o solo algunos efectos secundarios de la importación?

Necesita el efecto secundario de la importación. La var se agregó para no eliminar la importación en el código transpilado.

entonces, ¿por qué no agregar

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

en la salida, la última importación: import "react" no se eliminará.

Desafortunadamente, esto no funciona y todavía obtengo:

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)

Creo que la razón aquí son las "dependencias débiles" en Meteor, que dependen de _callee_ para proporcionar todas las referencias necesarias para ellas.

Recientemente comencé a aprender mecanografiado para Angular 2. Esto me ha sorprendido varias veces, como estoy seguro de que lo hará con otros recién llegados. Entiendo que se trata de una optimización, pero en realidad es muy confuso. Si escribo algo en mi archivo ts, espero que produzca, genere una salida o algún tipo, no simplemente lo ignore. Entiendo que puedo forzar la producción de la declaración require, pero eso parece un poco complicado. Esto parece ser una causa en la que yo, como usuario, debería poder tomar mis propias decisiones sin que el compilador decida qué es lo mejor.

TypeScript superpone la declaración de tipo sobre los valores, por lo que la misma sintaxis de importación le da tipos, valores o ambos. esto hace que sea muy conveniente e intuitivo trabajar con el módulo; si necesita un tipo de otro módulo, simplemente impórtelo a medida que importa una variable, o simplemente importe la clase y utilícelo como tipo y como constructor con new.

La otra cara de la moneda es que puede importar a módulos que no existen, ya que son solo contenedores de tipo. si el compilador emitió referencias a estos módulos, fallará en tiempo de ejecución. La solución que tenemos es detectar _cómo_ se usó la importación, y _todas_ las referencias a ella se usan como tipos, entonces no es necesario en tiempo de ejecución y se elide.

obviamente, esto puede ser confuso si también necesita los efectos secundarios de un módulo. pero al revés también es confuso si se emitió una importación a un módulo no existente, deje que un lado tenga que perder la conveniencia de agrupar tipos y valores en la misma sintaxis.

Una solución alternativa:
Si se usa la resolución tradicional ( moduleResolution no se establece en node ), nunca se eludirá una declaración de importación que contenga ! en su identificador. Creo que esto tiene algo que ver con que intentemos respaldar mejor un comportamiento de systemjs , pero se puede usar para forzar una importación. Si _est_ usando systemjs, requirejs o cualquier cargador que permita la reasignación de identificadores de módulo, puede finalizar su importación forzada en !js o una marca similar y mapearla con su cargador de módulo. TS emitirá la importación palabra por palabra (y no intentará revisarla o resolverla), y se le puede enseñar a su cargador cómo comprender la importación.

Parece que el equipo ya tiene que crear un código de caso especial para este tipo de importaciones, como en 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 señala @mhegazy , esto no se trata solo de optimización, sino de evitar requerir una importación sin una definición de tiempo de ejecución (por ejemplo, Interfaz) que causaría un error de tiempo de ejecución.

Me pregunto qué sucederá con más frecuencia. Podría detectar fácilmente si la importación es un * .d.ts, por lo que se eliminarían fácilmente.

En el resto de los casos, parece bastante trivial que el compilador detecte que el módulo importado no tendría salida en tiempo de ejecución y solo eludiría esas importaciones.

Desde una perspectiva SECA , esta sugerencia me dejó deseando tener un transpilador para que tu transpilador escribiera este código duplicado por mí.

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

A mi modo de ver, debería ser la elección del archivo _importado_ si necesita cargarse en tiempo de ejecución (porque sabe que tiene efectos secundarios). Esto realmente no debería ser decidido por el archivo _loading_ (es decir, el archivo con la declaración de importación), ya que no es asunto suyo conocer los detalles de implementación de otros archivos / módulos.

Al menos esto funcionaría bien desde la perspectiva de angularjs, donde un archivo que define directivas y servicios "sabe" que debe cargarse en tiempo de ejecución si se hace referencia a él, por lo que el compilador no debe omitirlo. Algún tipo de sugerencia en un archivo para el compilador de mecanografiado para "no elide" arreglaría este IMO. Pero probablemente estoy pasando por alto algún caso de uso aquí.

-JM

xmodule.ts:

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

Si xmodule tiene declaraciones, por ejemplo, la declaración console.log, por definición no es solo una pila de declaraciones. No debería ser responsabilidad del módulo de importación hacer declaraciones que usen xmodule para que xmodule sea considerado más que una serie de declaraciones de tipo.

@weswigham

Mencionaste usar un bang para forzar tu importación. Eso es lo preferido para mi escenario. Sin embargo, ¿cómo lo uso? Intenté todo lo siguiente:

!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'!;

Estaba en la cadena, pero creo que el comportamiento se eliminó recientemente cuando se introdujeron las asignaciones de ruta.

Estoy usando la biblioteca, como react, para importar el contenedor de nodos - h (en reaccionar es react () si no me equivoco), así que importo así:

import { h, Component } from "preact";

Entonces, por supuesto, no tengo h en el tiempo de ejecución. Porque, ¿por qué estaría allí? Además, usar con jsx preserve, por lo que TypeScript realmente no se trata de h, pero babel lo sabe.

Entonces, lo que queda, usando una referencia como h; después de la importación, ¿sí?

Para h (o cualquier otra fábrica jsx personalizada) use --jsxFactory para que el compilador sepa que esta es la fábrica que está usando en tiempo de ejecución. por ejemplo, --jsxFactory h .

Esto no requiere typescript@next por el momento, debería estar disponible en TypeScript 2.1.2.

@mhegazy Para proyectos que tienen dos tipos de consumidor JSX (como react y snabbdom) no es aceptable.

Estoy trabajando en un proyecto que usa react para renderizar la interfaz de usuario web y un dom virtual personalizado implementado por nosotros mismos para renderizar objetos webgl. Y llega al caso de la esquina, ya que NECESITAMOS dos tipos diferentes de anotación @jsx para el mismo proyecto.

Ahora me veo obligado a exportar nuestro propio h como variable global ... Es feo.

Inclúyame en la lista de desarrolladores que necesitan / quieren una importación de una sola línea que haga esto. Dos líneas de código fuente para lograrlo es lo que estamos tratando de eliminar, especialmente cuando queremos importar muchos módulos seguidos. Supongo que lo que me sonó mejor hasta ahora es una palabra clave / sintaxis para forzar la importación. ¡Gracias de nuevo chicos!

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

CyrusNajmabadi picture CyrusNajmabadi  ·  3Comentarios

fwanicka picture fwanicka  ·  3Comentarios

bgrieder picture bgrieder  ·  3Comentarios

blendsdk picture blendsdk  ·  3Comentarios

siddjain picture siddjain  ·  3Comentarios