Typescript: Import erzwingen

Erstellt am 4. Sept. 2014  ·  31Kommentare  ·  Quelle: microsoft/TypeScript

Ich habe viele Szenarien, in denen ich dies tun würde:

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

Ich verwende nicht myExternalModule in meinem Code, aber ich möchte trotzdem, dass es mit requirejs aufgenommen wird. Ich brauche es nur, um dort zu sein.
Wenn es ein forceimport Schlüsselwort geben könnte, wäre das sehr cool!

Question

Hilfreichster Kommentar

Warum also nicht einfach hinzufügen?

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

In der Ausgabe wird der letzte Import: import "react" nicht entfernt.

Alle 31 Kommentare

Es sollte möglich sein, die Ausgabe durch Schreiben zu erzwingen

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

Dies wird sich jedoch nicht beschweren, wenn "./myExternalModule" ein falscher Pfad ist.

Der richtige Ansatz besteht darin, eine Dummy-Referenz einzuführen, die keine Nebenwirkungen hat:

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

Obwohl dies ein Hack ist, ist es sehr unwahrscheinlich, dass für diesen eher engen Anwendungsfall eine neue Syntax eingeführt wird. Siehe die Designziele.

Wenn TypeScript den Pfad in var myExternalModule = require("./myExternalModule"); überprüfen würde, könnten Benutzer die Überprüfung zur Kompilierungszeit erhalten, ohne auf den Hack zurückgreifen zu müssen.

Der var -Ansatz importiert die Typen jedoch nicht aus dem Modul.

Außerdem können Sie bei Bedarf das Attribut amd-dependency verwenden.

Zu welchem ​​Zweck importieren Sie Dinge, die nicht verwendet werden?

Zum Beispiel habe ich in einer eckigen App einige Anweisungen in anderen Dateien. Ich muss diese Anweisungen importieren, sonst funktioniert mein HTML-Markup nicht. Ich benutze sie jedoch nicht in JavaScript.
Normalerweise sind die meisten Module im Winkel entkoppelt und haben keine Abhängigkeiten voneinander, sondern müssen importiert werden, damit die gesamte App funktioniert.

: +1:
Es verwirrt mich.
Warum eliminiert der TypeScript-Compiler das?
Das externe Modul ist kein nicht instanziiertes Modul.
erfordern hat eine Nebenwirkung.
Die Importklausel scheint mit den Anforderungen von AMD und CommonJS kompatibel zu sein. aber es ist nicht.

Dieses Problem ist auch ärgerlich, wenn Module benötigt werden, die nichts anderes exportieren als Shim-Funktionen für globale Objekte wie es5-shim , es6-shim und viele andere.

Es verwirrt mich.
Warum eliminiert der TypeScript-Compiler das?

Dies ist eine Optimierung, da manchmal nur die Typinformationen aus einem Modul importiert werden sollen:

import foo = require('foo');

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

Es ist nicht nur eine Optimierung. Wenn Sie ein importiertes Modul nur für Typen verwenden (dh an keiner Wertposition verwendet), können wir die erforderliche Logik dafür nicht ausgeben, da das Modul ein Nur-Umgebungs-Typmodul sein kann, das zur Laufzeit nicht vorhanden ist. Das Auslassen eines Modulimports würde einen Laufzeitfehler beim Laden eines nicht vorhandenen Moduls bedeuten.

Sie können hierfür das Flag amd-dependency verwenden:

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

import x = require('foo');

Anstelle von <amd-dependency> was wie ein Hack aussieht, wäre es interessant gewesen, <reference name="..."> IMO zu verwenden.

@danquirk Es ist eine willkürliche und gefährliche Sache, wenn die TypeScript-Engine die Verantwortung für die Optimierung der Importanweisungen übernimmt. Es gibt viele Muster, die TypeScript noch nicht aus JavaScript erfasst. Ein Beispiel ist, wie nicht angesprochen wird, dass this einen Typ # 229 haben könnte.

Der Import wird zum Laden von Abhängigkeiten verwendet, bevor der aktuelle Code geladen wird, und wird möglicherweise nicht direkt referenziert. Winkelabhängigkeiten sind ein Beispiel, während jQuery-Plugins ein anderes sind. Jede Bibliothek, die ein Basisobjekt erweitert und nicht direkt referenziert wird, ist von "Feature" betroffen. Wenn Sie sich dafür entscheiden, einen Import basierend auf lokaler statischer Analyse willkürlich nicht einzuschließen, legen Sie ein Compilermuster im C-Stil über die explizite Absicht des Entwicklers, der die Importanweisung geschrieben hat. Die Erwartung beim Schreiben des Imports besteht darin, dass er als Abhängigkeit aufgenommen wird und dem lokalen Bereich des Moduls zur Verfügung steht. Jede andere Aktion ist ein Nebeneffekt der natürlichen Erwartung, import in ein Modul zu schreiben.

Es ist sehr wahrscheinlich, dass der TypeScript-Compiler in diesem Fall weniger und mehr leistet.

Die nächste Version von TypeScript unterstützt Module im ES6-Stil (die Änderung befindet sich derzeit im Master). Wenn Sie also nur die Abhängigkeit deklarieren möchten, können Sie Folgendes verwenden:

import "myLib";

Der Compiler wird diesen Import nicht ausschließen.

Wird das bestehende inkonsistente Verhalten angegangen oder bleibt es etwas, das die Leute entdecken können? Sollte der Dokumentation hinzugefügt werden, dass es sich um einen Fall handelt, in dem import die Datei nicht als Abhängigkeit hinzufügt? Wird beim Erstellen von TypeScript AMD-Modulen ///<reference... jetzt als Fehler betrachtet?

Die Art und Weise, wie import derzeit funktioniert, ist schlechtes Design. Schlechtes Design schafft Mehrdeutigkeit. Wenn das zusätzliche Verhalten nicht klar definiert ist, wird es höchstwahrscheinlich entdeckt, obwohl es eine Nebenwirkung ist. Daher das OP, ich und andere Kommentare und Fehler, die sich in diesem Forum darauf beziehen.

Bei Verwendung der Abhängigkeitsverwaltung für Common oder Require-Stil ist bereits eine Reihe von Vorgehensweisen vorhanden. Die Implementierung von import zeigt, dass es ignoriert wurde.

Informationen zur Dokumentation finden Sie in den Daten in Abschnitt 11.2.5 und Abschnitt 11.2.6 :

Eine externe Importdeklaration wird im generierten JavaScript als Variable dargestellt, die durch einen Aufruf der vom Modulsystemhost bereitgestellten Funktion 'require' initialisiert wird. Eine Variablendeklaration und ein 'require'-Aufruf werden für ein bestimmtes importiertes Modul nur ausgegeben, wenn das importierte Modul oder ein lokaler Alias ​​(Abschnitt 10.3), der auf das importierte Modul verweist, irgendwo im Hauptteil des importierenden Moduls als PrimaryExpression bezeichnet wird. Wenn ein importiertes Modul nur als ModuleName oder TypeQueryExpression referenziert wird, wird nichts ausgegeben.

/// Referenzen sind nicht dasselbe wie Importe. /// Referenzen bringen zusätzliche Deklarationen in Ihren globalen Bereich, z. B. dom APIs. Es ist eine Anweisung an den Compiler, der Existenz dieser Entitäten zu vertrauen, und sie hat keine Auswirkungen auf den generierten Code.

Importe hingegen müssen als erforderliche Anweisungen ausgegeben werden. Wenn Sie einen Import nur an einer Typposition verwenden, ist die Absicht nicht klar. Entweder möchten Sie nur die Typen (Konstruktionszeitkonstrukte, die zur Laufzeit nicht vorhanden sind), und in diesem Fall möchten Sie, dass der Import entfernt wird Wenn dies nicht der Fall ist, würden Sie möglicherweise ein nicht vorhandenes Nur-Typ-Modul importieren. Oder Sie möchten auch die Nebenwirkungen. Für letztere scheint die Syntax import "mod"; eine klarere Syntax zu sein, um die Absicht zu deklarieren.

@mhegazy Ich möchte keine neue Ausgabe eröffnen, aber dies ist ein
Betrachten Sie den folgenden Code, wenn er zu js kompiliert wird, wird die Referenz React entfernt, da sie nicht im Code erscheint und der Code fehlschlägt, da ReactRouter davon abhängt. Das Update ... wirklich dumm ist unter dem Code. Gibt es eine intelligentere Möglichkeit, dies zu tun? Vielen Dank.

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

Der Code schlägt fehl, da ReactRouter davon abhängt.

Benötigt es die Variable "React" oder nur einige Nebenwirkungen des Imports?

Es braucht den Nebeneffekt vom Import. Die Variable wurde hinzugefügt, um den Import im transpilierten Code nur nicht zu entfernen.

Warum also nicht einfach hinzufügen?

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

In der Ausgabe wird der letzte Import: import "react" nicht entfernt.

Leider funktionieren diese Wünschelruten nicht und ich bekomme immer noch:

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)

Ich denke, dass der Grund hier die "schwachen Abhängigkeiten" in Meteor sind, die von _callee_ abhängen, um alle notwendigen Referenzen für sie bereitzustellen.

Ich habe vor kurzem angefangen, Typoskript für Angular 2 zu lernen. Dies hat mich einige Male überrascht, da ich mir sicher bin, dass dies auch für andere Neuankömmlinge der Fall sein wird. Ich verstehe, dass dies eine Optimierung ist, aber es ist tatsächlich sehr verwirrend. Wenn ich etwas in meine ts-Datei eingebe, erwarte ich, dass es produziert, ausgegeben oder auf irgendeine Art nicht einfach ignoriert wird. Ich verstehe, dass ich die Erstellung der erforderlichen Anweisung erzwingen kann, aber das scheint wenig hackig zu sein. Dies scheint eine Ursache zu sein, bei der ich als Benutzer in der Lage sein sollte, meine eigenen Entscheidungen zu treffen, ohne dass der Compiler entscheidet, was am besten ist.

TypeScript überlagert die Typdeklaration mit den Werten, sodass Sie mit derselben Importsyntax Typen, Werte oder beides erhalten. Dies macht es sehr bequem und intuitiv, mit Modulen zu arbeiten. Wenn Sie einen Typ aus einem anderen Modul benötigen, importieren Sie ihn einfach beim Importieren einer Variablen oder importieren Sie die Klasse und verwenden Sie ihn sowohl als Typ als auch als Konstruktor mit new.

Die Kehrseite davon ist, dass Sie Importe in Module durchführen können, die nicht vorhanden sind, da es sich nur um Typcontainer handelt. Wenn der Compiler Verweise auf diese Module ausgegeben hat, schlägt dies zur Laufzeit fehl. Die Lösung, die wir haben, besteht darin, zu erkennen, wie der Import verwendet wurde, und alle Verweise darauf werden als Typen verwendet. Dann wird er zur Laufzeit nicht benötigt und entfernt.

Dies kann natürlich verwirrend sein, wenn Sie auch die Nebenwirkungen eines Moduls benötigen. Der umgekehrte Weg ist jedoch auch verwirrend, wenn ein Import in ein nicht vorhandenes Modul ausgegeben wurde, sodass eine Seite die Bequemlichkeit verlieren muss, Typen und Werte in derselben Syntax zu bündeln.

Eine Problemumgehung:
Wenn Sie die herkömmliche Auflösung verwenden ( moduleResolution ist nicht auf node ), wird eine Importanweisung, deren Bezeichner ! , niemals umgangen. Ich glaube, das hat etwas damit zu tun, dass wir versuchen, ein Verhalten von systemjs besser zu unterstützen, aber es kann verwendet werden, um einen Import zu erzwingen. Wenn Sie systemjs, requirejs oder einen Loader verwenden, der die Neuzuordnung von Modulkennungen ermöglicht, können Sie den erzwungenen Import in !js oder einer ähnlichen Markierung beenden und ihn Ihrem Modulloader zuordnen. TS gibt den Import wörtlich aus (und versucht nicht, ihn zu überprüfen oder aufzulösen), und Ihrem Loader kann beigebracht werden, wie er den Import versteht.

Es scheint, dass das Team bereits Sonderfallcode für diese Art von Importen erstellen muss, z. B. in 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;
    }
}

Wie @mhegazy hervorhebt , geht es hier nicht nur um Optimierung, sondern vielmehr darum, zu vermeiden, dass ein Import ohne Laufzeitdefinition (z. B. Schnittstelle) erforderlich ist, der einen Laufzeitfehler verursachen würde.

Ich frage mich, was öfter passieren wird. Sie können leicht erkennen, ob es sich bei dem Import um eine * .d.ts handelt, sodass diese leicht entfernt werden können.

In den übrigen Fällen scheint es für den Compiler ziemlich trivial zu sein, zu erkennen, dass das importierte Modul keine Laufzeitausgabe hat, und nur diese Importe zu eliminieren.

Aus DRY- Sicht wünschte ich mir, ich hätte einen Transpiler für Ihren Transpiler, um diesen duplizierten Code für mich zu schreiben.

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

So wie ich es sehe, sollte es die Wahl der importierten Datei sein, ob sie zur Laufzeit geladen werden muss (weil sie weiß, dass sie Nebenwirkungen hat). Dies sollte wirklich nicht von der _loading_-Datei (dh der Datei mit der import-Anweisung) entschieden werden, da es nicht seine Aufgabe ist, Implementierungsdetails anderer Dateien / Module zu kennen.

Zumindest würde dies aus der Perspektive von anglejs gut funktionieren, wenn eine Datei, die Direktiven und Dienste definiert, "weiß", dass sie zur Laufzeit geladen werden muss, wenn auf sie verwiesen wird, damit der Compiler sie nicht umgehen kann. Eine Art Hinweis in einer Datei an den Typoskript-Compiler, "nicht zu entfernen", würde diese IMO beheben. Aber ich übersehen hier wahrscheinlich einen Anwendungsfall.

-JM

xmodule.ts:

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

Wenn xmodule Anweisungen hat, z. B. die Anweisung console.log, handelt es sich per Definition nicht nur um einen Stapel von Deklarationen. Es sollte nicht in der Verantwortung des Importmoduls liegen, Anweisungen zu erstellen, die xmodule verwenden, damit xmodule mehr als nur eine Reihe von Typdeklarationen betrachtet.

@weswigham

Sie haben erwähnt, dass Sie einen Knall verwenden, um Ihren Import zu erzwingen. Das wird für mein Szenario bevorzugt. Wie benutze ich es aber? Versuchte alle der folgenden:

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

Es war in der Zeichenfolge - aber ich denke, das Verhalten wurde kürzlich entfernt, als Pfadzuordnungen eingeführt wurden.

Ich verwende eine Bibliothek wie "Reagieren" zum Importieren des Knoten-Wrappers - h (in "Reagieren" ist es "Reagieren" (), wenn ich mich nicht irre), also importiere ich wie folgt:

import { h, Component } from "preact";

Dann habe ich natürlich kein h in der Laufzeit. Denn warum sollte es dort sein? Auch mit jsx bewahren, so dass TypeScript wirklich nicht über h, aber Babel weiß dann.

Also, was bleibt übrig, unter Verwendung einer Referenz wie h; nach dem import ja?

Verwenden Sie für h (oder eine andere benutzerdefinierte jsx-Factory) --jsxFactory , um den Compiler darüber zu informieren, dass dies die Factory ist, die Sie zur Laufzeit verwenden. zB --jsxFactory h .

Nicht, dass dies momentan typescript@next erfordert, sollte in TypeScript 2.1.2 verfügbar sein.

@mhegazy Für Projekte mit zwei Arten von JSX-Consumer (wie React und Snabbdom) ist dies nicht akzeptabel.

Ich arbeite an einem Projekt, das React zum Rendern der Web-Benutzeroberfläche und eines von uns implementierten benutzerdefinierten virtuellen Dom zum Rendern von Webgl-Objekten verwendet. Und es trifft den Eckfall, da wir zwei verschiedene Arten von @jsx -Anmerkungen für dasselbe Projekt benötigen.

Jetzt bin ich gezwungen, unser eigenes h als globale Variable zu exportieren ... Es ist hässlich.

Nehmen Sie mich in die Liste der Entwickler auf, die einen Einzeiler-Import benötigen / wollen, der dies tut. Zwei Quellzeilen, um dies zu erreichen, versuchen wir zu beseitigen, insbesondere wenn wir viele Module hintereinander importieren möchten. Ich denke, was für mich bisher am besten klang, ist ein Schlüsselwort / eine Syntax, um den Import zu erzwingen. Nochmals vielen Dank Jungs!

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen