Typescript: 强制进口

创建于 2014-09-04  ·  31评论  ·  资料来源: microsoft/TypeScript

我在许多情况下会这样做:

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

我没有在代码中使用myExternalModule ,但我仍然希望使用requirejs将其包含在内。 我只需要它在那儿。
如果可以有一个forceimport关键字,那将非常酷!

Question

最有用的评论

所以为什么不只是添加

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

在输出中,最后一个导入: import "react"不会被忽略。

所有31条评论

应该可以通过书写来强制发射

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

但这不会抱怨“ ./myExternalModule”是不正确的路径。

正确的方法是引入没有副作用的虚拟引用:

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

尽管这是一个hack,但不太可能为这种相当狭窄的用例引入新的语法。 查看设计目标。

如果打字稿将验证路径var myExternalModule = require("./myExternalModule");那么这将允许用户迫使EMIT _and _ GET编译时间验证,而不必诉诸到弓。

不过, var方法不会从模块中导入类型。

另外,如果需要,您可以使用amd-dependency属性。

您要导入什么目的不使用的东西?

例如,在有角度的应用程序中,我在其他文件中有一些指令。 我需要导入这些指令,否则我的html标记将无法正常工作。 我虽然没有在JavaScript中使用它们。
通常情况下,大多数模块都是分离的,彼此之间没有依赖关系,但是需要导入才能使整个应用程序正常工作。

:+1:
这让我感到困惑。
为什么TypeScript编译器会消除它?
外部模块不是非实例化模块。
需要有副作用。
import子句似乎与AMD和CommonJS的要求兼容。 但事实并非如此。

当require()-ing模块实际上不导出任何内容而是填充全局对象(例如es5-shimes6-shim等)上的功能时,此问题也很烦人。

这让我感到困惑。
为什么TypeScript编译器会消除它?

这是一种优化,因为有时我们只想从模块中导入类型信息:

import foo = require('foo');

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

这不仅仅是一种优化。 如果您仅将导入的模块用于类型(即未在任何值位置使用),则无法为其发出需求逻辑,因为该模块可以是运行时不存在的仅环境类型的模块。 发出模块导入将意味着尝试加载不存在的模块时发生运行时错误。

您可以为此使用amd-dependency标志:

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

import x = require('foo');

代替<amd-dependency>看起来有点像黑客一样,使用<reference name="..."> IMO会很有趣。

@danquirk让TypeScript引擎负责优化import语句是一件任意且危险的事情。 TypeScript尚未从JavaScript捕获许多模式。 一个示例是它如何解决this可能具有类型#229的问题。

导入用于在加载当前代码之前加载依赖项,并且可能无法直接引用。 角度依赖是一个示例,而jQuery插件是另一个示例。 任何扩展基础对象且未直接引用的库都将受到“功能”的影响。 通过决定不基于本地静态分析任意包含导入,您将C样式编译器模式强加于编写了import语句的开发人员的明确意图。 编写导入的期望是将其作为依赖项包括在内并且可用于模块的本地范围。 任何其他操作都会对选择在模块中写入import的自然期望产生副作用。

在这种情况下,TypeScript编译器很有可能做得更少,可以完成更多工作。

TypeScript下一个版本将支持ES6样式模块(当前在主版本中进行更改)。 因此,如果您要声明的是依赖性,则可以使用:

import "myLib";

编译器不会忽略此导入。

是否会解决现有的不一致行为,还是让人们发现它仍然是一件有趣的事情? 是否应该在文档中添加import不会将文件添加为依赖项的情况? 在构建TypeScript AMD模块时,现在将///<reference...视为错误吗?

import当前工作的方式是错误的设计。 不良的设计会产生歧义。 如果没有明确定义其他行为,则尽管有副作用,但很可能会发现它。 因此,在本论坛中,OP,我本人以及与之相关的其他评论和错误。

使用“通用”或“需要”样式依赖项管理时,已经存在一种现有的实践体。 import表明它被忽略了。

关于文档,请参考规范第11.2.5

外部导入声明在生成的JavaScript中表示为变量,该变量通过调用模块系统主机提供的'require'函数进行初始化。 仅当导入的模块或引用导入的模块的本地别名(第10.3节)_在导入模块的主体中​​被引用为PrimaryExpression时,才会为特定的导入模块发出变量声明和'require'调用。 如果导入的模块仅作为ModuleName或TypeQueryExpression引用,则不会发出任何内容。

///引用与导入不同。 ///引用将其他声明引入您的全局范围,例如dom API。 这是对编译器的声明,以信任这些实体的存在,并且对生成的代码没有影响。

另一方面,导入需要作为require语句发出。 如果仅在类型位置使用导入,则意图不明确,要么只需要类型(在运行时不存在的设计时构造),在这种情况下,您希望忽略导入,如果不是,则可能要导入一个不存在的仅类型模块。 或者您也想要副作用。 对于后者, import "mod";语法似乎是更清晰的声明意图的语法。

@mhegazy我不想打开一个新的问题,但这在React世界中有点问题。
考虑下面的代码,当编译为js时, React引用将被删除,因为它没有出现在代码中,并且由于ReactRouter依赖于此,该代码将失败。 该修复程序...愚蠢的是下面的代码。 有没有更聪明的方法来消除呢? 谢谢。

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

该代码将失败,因为ReactRouter依赖它。

是否需要var“ React”或从导入中获取一些副作用?

它需要导入的副作用。 添加var只是为了不删除已编译代码中的导入。

所以为什么不只是添加

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

在输出中,最后一个导入: import "react"不会被忽略。

不幸的是,这道菜不起作用,我仍然得到:

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)

我认为这里的原因是流星中的“弱依赖项”,它依赖_callee_为它们提供所有必要的引用。

我最近已经开始学习Angular 2的打字稿。这使我感到困惑不已,因为我相信其他新手也可以。 我知道这是一种优化,但实际上非常令人困惑。 如果我在ts文件中键入内容,我希望它可以产生,输出或某种形式,而不仅仅是忽略它。 我知道我可以强制执行require语句,但这似乎有点不客气。 这似乎是一个原因,在这种情况下,我(作为用户)应该能够在无需编译器确定最佳选择的情况下做出自己的决定。

TypeScript将类型声明覆盖在值的顶部,因此相同的导入语法可为您提供类型,值或两者。 这使得使用模块非常方便和直观; 如果需要其他模块中的类型,则可以在导入变量时将其导入,也可以仅在导入类时将其用作类型和new的构造函数。

另一方面,您可以将不存在的模块导入,因为它们只是类型容器。 如果编译器发出对这些模块的引用,它将在运行时失败。 我们所拥有的解决方案是检测使用了_how_的导入方式,并且将对它的_all_引用用作类型,然后在运行时不需要它并消除了它。

显然,如果您还需要模块的副作用,这可能会造成混淆。 但是相反,如果发出了对不存在的模块的导入,也将造成混乱,让一方不得不放弃使用相同语法将类型和值绑定在一起的便利。

解决方法:
如果使用传统解析( moduleResolution未设置为node ),则不会忽略在标识符中包含!的import语句。 我认为这与我们试图更好地支持某些systemjs行为有关,但是可以用来强制导入。 如果您使用systemjs,requirejs或允许重新映射模块标识符的任何加载器,则可以在!js或类似标记中结束强制导入,并使用模块加载器将其映射。 TS将逐字逐行地导入导入(而不是尝试进行类型检查或解析),并且可以教会您的加载程序如何理解导入。

看来该团队已经必须为这些类型的导入制作特殊情况的代码,例如在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;
    }
}

正如@mhegazy指出的,这不仅是优化,还在于避免要求没有运行时定义的导入(例如Interface),这会导致运行时错误。

我怀疑这会更经常发生。 您可以轻松地检测到导入是否为* .d.ts,以便轻松删除它们。

在其余情况下,对于编译器来说,检测导入的模块将没有运行时输出而仅忽略那些导入似乎是一件微不足道的事情。

DRY的角度来看,这个建议让我希望我有一个编译器供您的编译器为我编写此重复的代码。

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

我的看法是,_imported_文件的选择是是否需要在运行时加载(因为它知道它有副作用)。 实际上,这不应该由_loading_文件(即带有import语句的文件)确定,因为了解其他文件/模块的实现细节不是我们的责任。

至少从angularjs的角度来看,这将很好地工作,在该角度下,定义指令和服务的文件“知道”如果被引用,则必须在运行时加载它,因此编译器不得忽略它。 文件到打字稿编译器的某种提示“不会消失”将解决此IMO。 但是我可能在这里忽略了一些用例。

-JM

xmodule.ts:

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

如果xmodule有语句,例如console.log语句,那么根据定义,它不只是一堆声明。 为了使xmodule不仅被视为一系列类型声明,使用xmodule的语句不应该由导入模块负责。

@weswigham

您提到使用爆炸来强制导入。 这是我的方案的首选。 但是我该如何使用呢? 尝试了以下所有条件:

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

它在字符串中-但我认为最近在引入路径映射时已删除了该行为。

我正在使用像react这样的库来导入节点包装器-h(如果我没记错的话,它是react()),所以我这样输入:

import { h, Component } from "preact";

然后,当然,我在运行时没有任何帮助。 因为为什么会在那里? 另外,与jsx保留一起使用,因此TypeScript确实与h无关,但是babel知道了。

因此,剩下的,使用像h这样的引用; 导入后,是吗?

对于h (或任何其他自定义jsx工厂),请使用--jsxFactory让编译器知道这是您在运行时使用的工厂。 例如--jsxFactory h

目前这还不需要typescript@next ,应该在TypeScript 2.1.2中可用。

@mhegazy对于具有两种JSX使用者(例如react和snabbdom)的项目,这是不可接受的。

我正在一个项目中,该项目使用react来呈现Web UI和由我们自己实现的自定义虚拟dom来呈现webgl对象。 由于我们需要为同一个项目使用两种不同类型的@jsx批注,因此它碰到了极端情况。

现在,我不得不将自己的h导出为全局变量...这很丑。

将我包括在需要/想要执行一次班轮导入的开发人员列表中。 我们正试图消除两行实现这一目标的源代码,尤其是当我们要连续导入许多模块时。 我想到目前为止听起来最好的是强制导入的关键字/语法。 再次感谢你们!

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

dlaberge picture dlaberge  ·  3评论

MartynasZilinskas picture MartynasZilinskas  ·  3评论

DanielRosenwasser picture DanielRosenwasser  ·  3评论

zhuravlikjb picture zhuravlikjb  ·  3评论

bgrieder picture bgrieder  ·  3评论