Typescript: forcer l'importation

Créé le 4 sept. 2014  ·  31Commentaires  ·  Source: microsoft/TypeScript

J'ai de nombreux scénarios où je ferais ceci:

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

Je n'utilise pas myExternalModule dans mon code mais je veux quand même qu'il soit inclus en utilisant requirejs. J'en ai juste besoin pour être là.
S'il pouvait y avoir un mot-clé forceimport ce serait très cool!

Question

Commentaire le plus utile

alors pourquoi ne pas simplement ajouter

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

dans la sortie, la dernière importation: import "react" ne sera pas élidée.

Tous les 31 commentaires

Il devrait être possible de forcer l'émission en écrivant

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

mais cela ne se plaindra pas si "./myExternalModule" est un chemin incorrect.

L'approche correcte consiste à introduire une référence fictive qui n'a pas d'effets secondaires:

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

Bien qu'il s'agisse d'un hack, il est très peu probable qu'une nouvelle syntaxe soit introduite pour ce cas d'utilisation plutôt restreint. Voir les objectifs de conception.

Si TypeScript vérifiait le chemin dans var myExternalModule = require("./myExternalModule"); alors cela permettrait aux utilisateurs de forcer l'émission _et _ obtenir une vérification à la compilation, sans avoir à recourir au hack.

L'approche var n'importe cependant pas les types du module.

De plus, vous pouvez utiliser l'attribut amd-dependency si vous le devez.

Dans quel but importez-vous des choses qui ne sont pas utilisées?

Par exemple, dans une application angulaire, j'ai des directives dans d'autres fichiers. J'ai besoin d'importer ces directives sinon mon balisage html ne fonctionnera pas. Cependant, je ne les utilise pas en JavaScript.
Habituellement, en angulaire, la plupart des modules sont découplés et n'ont pas de dépendances les uns sur les autres, mais doivent être importés pour que l'ensemble de l'application fonctionne.

: +1:
Cela me déroute.
Pourquoi le compilateur TypeScript l'éliminer?
le module externe n'est pas un module non instancié.
require a un effet secondaire.
La clause d'importation semble compatible avec les exigences d'AMD et de CommonJS. mais ce n'est pas.

Ce problème est également ennuyeux lorsque require () - ing des modules qui n'exportent pas vraiment quoi que ce soit, mais plutôt des fonctionnalités de shim sur des objets globaux, comme es5-shim , es6-shim et bien d'autres.

Cela me déroute.
Pourquoi le compilateur TypeScript l'éliminer?

C'est une optimisation car parfois on souhaite importer uniquement les informations de type d'un module:

import foo = require('foo');

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

ce n'est pas seulement une optimisation. Si vous utilisez un module importé pour les types uniquement (c'est-à-dire qu'il n'est utilisé dans aucune position de valeur), nous ne pouvons pas émettre la logique requise pour celui-ci, car le module peut être un module de type uniquement ambiant qui n'existe pas à l'exécution. L'émission d'une importation de module signifierait une erreur d'exécution en essayant de charger un module non existant.

Vous pouvez utiliser l'indicateur amd-dependency pour cela:

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

import x = require('foo');

Au lieu de <amd-dependency> qui ressemble un peu à un hack, il aurait été intéressant d'utiliser <reference name="..."> IMO.

@danquirk C'est une chose arbitraire et dangereuse que le moteur TypeScript assume la responsabilité d'optimiser les instructions d'importation. Il existe de nombreux modèles que TypeScript ne capture pas encore à partir de JavaScript. Un exemple est comment il ne répond pas que this pourrait avoir un type # 229.

L'importation est utilisée pour charger les dépendances avant le chargement du code actuel et ne peut pas être référencé directement. Les dépendances angulaires en sont un exemple, tandis que les plugins jQuery en sont un autre. Toute bibliothèque qui étend un objet de base et n'est pas référencée directement est affectée par "fonctionnalité". En décidant de ne pas inclure arbitrairement une importation basée sur une analyse statique locale, vous imposez un modèle de compilateur de style C sur l'intention explicite du développeur qui a écrit l'instruction d'importation. L'attente de l'écriture d'import est qu'elle sera incluse en tant que dépendance et disponible pour la portée locale du module. Toute autre action est un effet secondaire à l'attente naturelle de choisir d'écrire import intérieur d'un module.

Il est très possible que dans ce cas, le compilateur TypeScript puisse faire moins et accomplir plus.

La prochaine version de TypeScript prendra en charge les modules de style ES6 (le changement est actuellement dans le master). donc si tout ce que vous voulez est de déclarer une dépendance, vous pouvez utiliser:

import "myLib";

Le compilateur n'élidera pas cette importation.

Le comportement incohérent existant sera-t-il abordé ou restera-t-il quelque chose d'amusant à découvrir pour les gens? Doit-il être ajouté à la documentation qu'il s'agit d'un cas où import n'ajoute pas le fichier en tant que dépendance? Lors de la construction de modules AMD TypeScript, ///<reference... sera-t-il désormais considéré comme une erreur?

La façon dont import fonctionne actuellement est une mauvaise conception. Une mauvaise conception crée une ambiguïté. Si le comportement supplémentaire n'est pas clairement défini, il sera probablement découvert par son effet secondaire. D'où l'OP, moi-même et d'autres commentaires et bugs qui y sont liés dans ce forum.

Il existe un corps de pratique existant qui existe déjà lors de l'utilisation de la gestion des dépendances de style Common ou Require. L'implémentation de import montre qu'il a été ignoré.

Concernant la documentation, veuillez vous référer à la section 11.2.5 et à la section 11.2.6 des spécifications :

Une déclaration d'importation externe est représentée dans le JavaScript généré comme une variable initialisée par un appel à la fonction «require» fournie par l'hôte système du module. Une déclaration de variable et un appel 'require' sont émis pour un module importé particulier uniquement si le module importé, ou un alias local (section 10.3) qui fait référence au module importé, est référencé comme une PrimaryExpression quelque part dans le corps du module d'importation. Si un module importé est référencé uniquement en tant que ModuleName ou TypeQueryExpression, rien n'est émis_.

/// les références ne sont pas les mêmes que les importations. /// references apporte des déclarations supplémentaires dans votre portée globale, par exemple les API dom. c'est une instruction au compilateur de faire confiance à l'existence de ces entités, et cela n'a pas d'impact sur le code généré.

Les importations, d'autre part, doivent être émises comme des déclarations d'exigence. dans le cas où vous n'utilisez qu'une importation dans une position de type, l'intention n'est pas claire, soit vous ne voulez que les types (qui sont des constructions au moment du design qui n'existent pas au moment de l'exécution), et dans ce cas, vous voulez que l'importation élide , et si ce n'est pas le cas, vous importeriez peut-être un module de type uniquement non existant. Ou vous voulez aussi les effets secondaires. Pour ce dernier, la syntaxe import "mod"; semble être une syntaxe plus claire pour déclarer l'intention.

@mhegazy Je ne veux pas ouvrir de nouveau numéro, mais c'est un petit problème dans le monde React.
Considérez le code ci-dessous, une fois compilé en js, la référence React est supprimée car elle n'apparaît pas dans le code et le code échouera car ReactRouter dépend. Le correctif ... vraiment stupide est en dessous du code. Y a-t-il un moyen plus intelligent de résoudre cela? Merci.

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 />});
  }
});

RÉPARER;) - EHM

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

le code échouera car ReactRouter en dépend.

a-t-il besoin du var "React" ou juste des effets secondaires de l'importation?

Il a besoin de l'effet secondaire de l'importation. Le var a été ajouté pour ne pas supprimer l'importation dans le code transpilé.

alors pourquoi ne pas simplement ajouter

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

dans la sortie, la dernière importation: import "react" ne sera pas élidée.

Malheureusement, cela ne fonctionne pas et je reçois toujours:

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)

Je pense que la raison ici sont les "dépendances faibles" dans Meteor, qui dépendent de _callee_ pour leur fournir toutes les références nécessaires.

J'ai récemment commencé à apprendre la dactylographie pour Angular 2. Cela m'a surpris un certain nombre de fois, comme je suis sûr que ce sera le cas pour d'autres nouveaux arrivants. Je comprends que c'est une optimisation mais c'est en fait très déroutant. Si je tape quelque chose dans mon fichier ts, je m'attends à ce qu'il le produise, qu'il sorte ou qu'il ne l'ignore pas simplement. Je comprends que je peux forcer la production de l'instruction require, mais cela semble un peu piraté. Cela semble être une cause où, en tant qu'utilisateur, je devrais être capable de prendre mes propres décisions sans que le compilateur ne décide de ce qui est le mieux.

TypeScript superpose la déclaration de type au-dessus des valeurs, de sorte que la même syntaxe d'importation vous donne des types, des valeurs ou les deux. cela rend le travail avec le module très pratique et intuitif; si vous avez besoin d'un type d'un autre module, importez-le simplement lorsque vous importez une variable, ou importez simplement la classe et utilisez-la à la fois comme type et comme constructeur avec new.

Le revers de la médaille, vous pouvez avoir des importations dans des modules qui n'existent pas, car ce ne sont que des conteneurs de type. si le compilateur a émis des références à ces modules, il échouera au moment de l'exécution. la solution que nous avons en place est de détecter _comment_ l'importation a été utilisée, et toutes les références à celle-ci sont utilisées comme types, alors ce n'est pas nécessaire au moment de l'exécution et élidé.

évidemment, cela peut être déroutant si vous avez également besoin des effets secondaires d'un module. mais l'inverse est également déroutant si une importation dans un module non existant a été émise, laissez un côté perdant la commodité de regrouper les types et les valeurs ensemble dans la même syntaxe.

Une solution de contournement:
Si vous utilisez la résolution traditionnelle ( moduleResolution n'est pas défini sur node ), une instruction d'importation contenant un ! dans son identifiant ne sera jamais éludée. Je pense que cela a quelque chose à voir avec le fait que nous essayons de mieux supporter un comportement systemjs , mais cela peut être utilisé pour forcer une importation. Si vous _are_ utilisez systemjs, requirejs ou tout autre chargeur qui permet le remappage des identificateurs de module, vous pouvez terminer votre importation forcée dans !js ou une marque similaire et la mapper avec votre chargeur de module. TS émettra l'importation mot pour mot (et n'essayera pas de le vérifier ou de le résoudre), et votre chargeur pourra apprendre à comprendre l'importation.

Il semble que l'équipe doive déjà créer un code de cas spécial pour ces types d'importations, comme dans 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;
    }
}

Comme le souligne @mhegazy , il ne s'agit pas seulement d'optimisation, mais plutôt d'éviter de nécessiter une importation sans définition d'exécution (par exemple, Interface) qui provoquerait une erreur d'exécution.

Je me demande ce qui arrivera plus souvent. Vous pouvez facilement détecter si l'importation est un * .d.ts afin que ceux-ci soient facilement éludés.

Dans le reste des cas, cela semble être une chose assez triviale pour le compilateur de détecter que le module importé n'aurait pas de sortie d'exécution et d'éliminer uniquement ces importations.

D'un point de vue DRY , cette suggestion m'a laissé souhaiter avoir un transpilateur pour votre transpilateur pour écrire ce code dupliqué pour moi.

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

La façon dont je le vois, ce devrait être le choix du fichier _imported_ s'il doit être chargé au moment de l'exécution (car il sait qu'il a des effets secondaires). Cela ne devrait vraiment pas être décidé par le fichier _loading_ (c'est-à-dire le fichier avec l'instruction d'importation), car ce n'est pas son affaire de connaître les détails d'implémentation d'autres fichiers / modules.

Au moins, cela fonctionnerait bien du point de vue angularjs, où un fichier qui définit des directives et des services "sait" qu'il doit être chargé au moment de l'exécution s'il est référencé, de sorte que le compilateur ne doit pas l'élider. Une sorte d'indication dans un fichier au compilateur dactylographié pour "ne pas éluder" corrigerait cet IMO. Mais j'oublie probablement un cas d'utilisation ici.

-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 a des instructions, .eg l'instruction console.log, ce n'est par définition pas simplement une pile de déclarations. Il ne devrait pas être de la responsabilité du module importateur de faire des déclarations qui utilisent xmodule pour que xmodule soit considéré plus qu'une simple série de déclarations de type.

@weswigham

Vous avez mentionné l'utilisation d'un bang pour forcer votre importation. C'est préféré pour mon scénario. Comment puis-je l'utiliser? J'ai essayé toutes les solutions suivantes:

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

C'était dans la chaîne - mais je pense que le comportement a été supprimé récemment lorsque les mappages de chemins ont été introduits.

J'utilise la bibliothèque, comme react, pour importer le wrapper de nœud - h (dans react c'est react () si je ne me trompe pas), donc j'importe comme ceci:

import { h, Component } from "preact";

Ensuite, bien sûr, je n'ai pas de h dans le runtime. Parce que pourquoi y serait-il? Aussi, utiliser avec jsx conserve, donc TypeScript ne parle vraiment pas de h, mais Babel le sait.

Donc, ce qui reste, en utilisant une référence comme h; après l'importation, oui?

Pour h (ou toute autre fabrique jsx personnalisée), utilisez --jsxFactory pour indiquer au compilateur qu'il s'agit de l'usine que vous utilisez au moment de l'exécution. par exemple --jsxFactory h .

Non, cela nécessite typescript@next pour le moment, devrait être disponible dans le TypeScript 2.1.2.

@mhegazy Pour les projets qui ont deux types de consommateurs JSX (tels que react et snabbdom), ce n'est pas acceptable.

Je travaille sur un projet qui utilise react pour rendre l'interface utilisateur Web et un dom virtuel personnalisé implémenté par nous-mêmes pour rendre les objets webgl. Et cela touche le cas du coin, car nous avons BESOIN de deux types différents d'annotation @jsx pour le même projet.

Maintenant, je suis obligé d'exporter notre propre h tant que variable globale ... C'est moche.

Incluez-moi dans la liste des développeurs qui ont besoin / veulent une importation d'une seule ligne qui le fasse. Deux lignes de source pour l'accomplir, c'est ce que nous essayons d'éliminer, en particulier lorsque nous voulons importer de nombreux modules à la suite. Je suppose que ce qui me paraissait le mieux jusqu'à présent, c'est un mot-clé / syntaxe pour forcer l'importation. Merci encore les gars!

Cette page vous a été utile?
0 / 5 - 0 notes