Typescript: 考虑允许从模块访问UMD全局变量

创建于 2016-08-05  ·  73评论  ·  资料来源: microsoft/TypeScript

#7125的反馈是,实际上有些人确实混合并匹配了全局和导入的UMD库,在实现此功能时,我们认为这不是可能的情况。

三种可能的选择:

  1. 做我们今天要做的事情-不好,因为没有好的解决方法
  2. 允许某些语法或配置说“此UMD全局实际上是全局可用的”-有点复杂,但可行
  3. 无论上下文如何,都允许访问所有UMD全局变量-会丢失一些错误,使人们忘记将UMD全局变量导入文件中。 这些大概有点罕见,但是错过它们真是愚蠢

听起来像可行,但可能不会:

  1. 将导入的UMD模块标记为“不可用于全局”-不好,因为在模块扩展期间会将UMD模块导入声明文件中。 从实现文件与声明文件导入的行为不同,这很奇怪。

为了简单起见,我倾向于选项3,但是如果可以在逻辑上使用一些合理的良好语法或配置,可以看到选项2。 如果有人想在TSLint规则中检测UMD全局变量的使用,将很简单。

一个前进的途径是实现选项3,如果结果是人们经常犯“忘记导入”错误,则添加一个tsconfig选项globals: []来明确指定允许哪些UMD全局变量。

Add a Flag Committed Suggestion good first issue help wanted

最有用的评论

+1。 我只是想将React与SystemJS一起使用,并且由于React的捆绑效果不佳,我只是直接从CDN的script标签中加载了React,因此React / ReactDOM对象在全球范围内都可以使用。

我正在按照模块的最佳做法编写代码,但是这将被捆绑(汇总)到一个在加载时执行的运行时脚本中。 必须import from react / react-dom,然后配置加载器说“不是真的,这些是全局变量”,这很痛苦(也是骗人的)(类似于https://中给出的示例WebPack配置) /www.typescriptlang.org/docs/handbook/react-&-webpack.html)。 在我的模块中简单地将这些作为全局变量可用会更容易(更准确)。 我尝试过的步骤(看起来很直观)是:

  1. npm install --save-dev @types/react @types/react-dom
  2. 在我的tsconfig.json中: "jsx": "react", "types": ["react", "react-dom"]
  3. 在我的模块中: export function MyComponent() { return <div>{"Hello, world"}</div>; }
  4. 同样: ReactDOM.render(...)

但是,这将导致错误React refers to a UMD global, but the current file is a module. Consider adding an import instead

如果这是可行的,那将比在模块中伪装代码而不是在模块中配置加载器/捆绑器要简单得多。 (或者,我可以通过添加一个包含以下内容的文件来达到预期的目的。现在我的模块可以将React和ReactDOM用作全局变量而不会出错,但这有点难看/骇客-尽管我可能有一个更简单的方法我错过了):

import * as ReactObj from "react";
import * as ReactDOMObj from "react-dom";

declare global {
    var React: typeof ReactObj;
    var ReactDOM: typeof ReactDOMObj;
}

所有73条评论

如果不通过///<reference types=<>>语法显式地“引用”(未导入)UMD模块,而不是允许它不受上下文限制地访问所有UMD全局变量,不是更简单吗? ,还是通过tsconfig.json中的types配置?

换句话说,为什么不只允许在模块中使用///<reference types=<>>

如果我们说/// <reference type="x" />表示x _everywhere_是全局可用的,那么通常是某些地方的.d.ts文件会错误地引用不是真正全局的东西(我可以告诉你,因为我一直在维护DefinitelyTyped的2.0分支,这是一个非常常见的错误)。

相反,如果该文件中只有_可用,那么您将不得不在各处复制和粘贴参考指令,这确实很烦人。 这些指令通常是幂等的,因此引入文件特定的行为很奇怪。

我懂了。 好吧,如果这不影响其他任何人,则最好保持当前行为。 我只需要完全过渡到将事物作为模块导入即可。

编辑:很高兴看到这确实影响了除我之外的许多人。

当前的理论是只要求人们迁移或不使用模块。 如果其他人遇到此问题,请留下您所使用的确切库的评论,以便我们进行更多调查。

我使用的是lodash,它本身没有类型。 我还遇到一种情况,在我的运行时环境中,最容易使用相对路径导入语句。 因此,我将导入语句与本地相对路径和文件夹相对对象(“ ./foo”以及“ N / bar”)结合使用。

如果我手动将@types/lodash/index.d.ts复制到node_modules/lodash/ ,则可以进行类型检查。

到目前为止,我的解决方法是使用///<amd-dependency path='../lodash' name="_"> (并且没有import语句)。 通过此组合,编译器将在全局范围内看到@ types / lodash定义,并且在发出的JS中仍具有正确的相对路径( ../lodash )。

我希望这是一个足以解决此问题的方案?

你能澄清一下吗

我还遇到一种情况,在我的运行时环境中,最容易使用相对路径导入语句。

如果我手动将@types/lodash/index.d.ts复制到node_modules/lodash/ ,则可以进行类型检查。

请? 我不熟悉这种情况,所以它的目的是什么,为什么有帮助?

嗨,大家好,

我正在寻找当前@types/bluebird声明问题的解决方案(请不要花时间阅读它)。 我发现,可以通过在.d.ts中添加export as namespace Promise;来解决问题,但是随后我遇到了此github问题描述的问题。

简而言之,我希望执行以下操作:

  1. git clone -b vanilla-es5-umd-restriction-problem https://github.com/d-ph/typescript-bluebird-as-global-promise.git
  2. cd typescript-bluebird-as-global-promise
  3. npm install
  4. 通过在export = Bluebird;行上方添加export as namespace Promise;编辑node_modules/@types/bluebird/index.d.ts
  5. npm run tsc

当前结果:
几个'Promise' refers to a UMD global, but the current file is a module. Consider adding an import instead.错误。

预期结果:
编译成功。

这个问题特别困难,因为它是由开发人员代码和第三方代码中的Promise用法触发的(在这种情况下为RxJS)。 后者假定Promise是全局的(由JS标准提供),因此将永远不会更改为使用import Promise from std; // (not that "std" is a thing)

我非常感谢将UMD模块既用作可导入模块又用作全局变量的方法。

谢谢。

-----------------------------更新

我最终以不同的方式解决了这个问题(即:通过PromisePromiseConstructor接口增强)。

"globals": [] tsconfig选项似乎比使它们在任何地方都可见更可取。 随着UMD声明成为规范,意外忘记导入模块的可能性很高。 请考虑保留当前行为。 这应该是一个错误。

有趣的是,我记得当时在点发布中删除了它们的全局window.moment变量。 我们以为我们已经明智地将其导入所有地方,但是我们忘记了大约5个地方。

当然,UMD软件包将在运行时在全局范围内可用,但何时可用取决于其他模块的加载顺序。

+1。 我只是想将React与SystemJS一起使用,并且由于React的捆绑效果不佳,我只是直接从CDN的script标签中加载了React,因此React / ReactDOM对象在全球范围内都可以使用。

我正在按照模块的最佳做法编写代码,但是这将被捆绑(汇总)到一个在加载时执行的运行时脚本中。 必须import from react / react-dom,然后配置加载器说“不是真的,这些是全局变量”,这很痛苦(也是骗人的)(类似于https://中给出的示例WebPack配置) /www.typescriptlang.org/docs/handbook/react-&-webpack.html)。 在我的模块中简单地将这些作为全局变量可用会更容易(更准确)。 我尝试过的步骤(看起来很直观)是:

  1. npm install --save-dev @types/react @types/react-dom
  2. 在我的tsconfig.json中: "jsx": "react", "types": ["react", "react-dom"]
  3. 在我的模块中: export function MyComponent() { return <div>{"Hello, world"}</div>; }
  4. 同样: ReactDOM.render(...)

但是,这将导致错误React refers to a UMD global, but the current file is a module. Consider adding an import instead

如果这是可行的,那将比在模块中伪装代码而不是在模块中配置加载器/捆绑器要简单得多。 (或者,我可以通过添加一个包含以下内容的文件来达到预期的目的。现在我的模块可以将React和ReactDOM用作全局变量而不会出错,但这有点难看/骇客-尽管我可能有一个更简单的方法我错过了):

import * as ReactObj from "react";
import * as ReactDOMObj from "react-dom";

declare global {
    var React: typeof ReactObj;
    var ReactDOM: typeof ReactDOMObj;
}

我也同意选项三加全局变量:[]备份。 对于新老用户来说,这似乎非常直观,并且可以提供人们所需的确切功能。

我不是代码方面的专家,因此我无法真正说出2是否更可取,但鉴于其配置简单明了,我认为这也很直观。

如果我想寻求帮助来实施这些方法,我应该去哪里?

这确实应该是一个标志。 这是一个巨大的重构危害。 即使默认情况下将标志指定为true。 我认为这必须在原始方案中继续有效,否则我们将失去UMD声明的主要优势。

React捆绑得不是很好

@billti您能详细说明吗?

必须import from反应/反应,然后配置加载程序说“不是真的,这些是全局变量”,这是一个痛苦(也是谎言)

我在教程中这样写的唯一原因是因为使用externals减少了捆绑时间。 如果在不导入的情况下使用全局React变量,则以后将无法轻松切换到模块,而导入为您提供了使用给定加载器的灵活性。

请参阅此问题(https://github.com/rollup/rollup/issues/855),了解有关他们如何尝试优化捆绑和观察到的大小的一个示例。 实际上,在我的设置中(使用汇总),我看到捆绑React的最小体积增长,所以我宁愿仅从CDN进行投放。 对我来说,它具有以下优点:

a)对我的网站的请求(和带宽)减少。
b)减少捆绑到我的构建链中的时间。
c)每次我推送更改时,减少在客户端上重新下载的代码(因为只有我的代码在重新下载的包中,而React仍在客户端缓存中未修改-从而获得304)。

在加载网站的Chrome开发工具中,React和React-dom(最小版本)在GZipped HTTP连接上只有47kb的网络流量,这比网站上的大多数图片都要少,所以我不是担心无论如何都要减少很多,除非会有很大的收获(例如减少50%)。

作为附录:我还要指出,没有此选项,您还将迫使人们使用捆绑这些导入的捆绑程序,因为TypeScript编译器本身没有配置说“此模块确实是全局的”,因此发出在运行时无法解析的模块的导入(或要求或定义)。

@billti SystemJS对此场景提供了完全支持。 您可以在开发过程中使用本地安装的软件包与在生产环境中使用CDN进行交换。 它还完全支持元数据,该元数据指定所有导入实际上应指示对全局变量的引用,该引用将在一次需要时被提取一次并附加到window对象,在生产中这可以来自CDN。 我还没有用react完成此操作,但是我已经用angular 1.x完成了

谢谢@aluanhaddad 。 有趣的是……我实际上是在尝试进行类似的工作,将我带到了这个障碍,并且无法弄清楚,所以就在今天早晨,我在SystemJS存储库上问了这个问题。 如果您可以回答如何实现https://github.com/systemjs/systemjs/issues/1510,那将真的很有帮助:-)

注意:我的另一条评论仍然表明,没有此,TypeScript本身的发射是不可用的,因为您需要诸如SystemJS / WebPack / Rollup等之类的东西,才能将导入映射到全局代码以运行代码。

我看一下是否可以创建一个有效的示例,我已经很长时间没有这样做了,并且我无法访问当时的源代码,但我百分百确定这是可能的。

第二点,这正是SystemJS所做的。 它将这些导入映射到全局,并了解实际上正在请求全局并且已经加载了全局。 输出绝对可用

仅供参考:我使用SystemJS API在SystemJS中进行了此工作,并将解决方案添加到https://github.com/systemjs/systemjs/issues/1510上。 谢谢。

再谈我的第二点:是的,我知道装载机正是可以做到的。 这就是我的观点,他们可以将导入的模块映射到全局,但是TypeScript不能-因此您必须使用加载程序来使代码在运行时有效。 因此,这是本期的第22个问题,您不能在模块中声明全局(在本例中为React)可用,您必须将其作为模块(不是)导入。 。

我的另一句话仍然表示,没有此,TypeScript本身的发射是不可用的,因为您需要诸如SystemJS / WebPack / Rollup等之类的东西,才能将导入映射到全局代码以运行代码。

@billti我不明白。 在哪种情况下,您唯一的选择是使用模块的全局版本,但TypeScript不允许您这样做? 我只看到场景中库可以作为一个全球性的模块。

@DanielRosenwasser我认为他的意思是,React实际上是运行时的全局对象,就像在全局对象的成员中一样,因为它是如何加载的。

@billti太棒了,它可以正常工作。

关于你的第二点:我明白你的意思了。

我想我的感觉是,在浏览器场景中,因为您需要使用诸如RequireJS之类的加载程序或诸如Webpack之类的打包程序,因为没有浏览器支持模块,但这没有任何区别。 (我听说Chakra在旗帜后面提供了它)。 因此,如果没有其他工具,根本无法运行代码。 包含definerequireSystem.register的输出有点像所发出的_JavaScript_代码不太可能是可移植的。 但是,我确实看到了“模块与非模块”区别的重要性。

您可以使用此替代方法至少只引用一次“模块”。

_shims.d.ts_

import __React from 'react';

declare global {
  const React: typeof __React;
}

然后,您可以在其他任何地方使用它,而无需导入它。
同样,这很明确,如果有点儿困惑,因为您说的是React已经成为全球性的,这也是您不必再导入它的原因。

重新输入您的shims.d.ts,如果您上了几篇文章,您会发现这就是我现在所做的(真主意也是如此);-)

我现在可以使它以几种方式之一工作,这不是重点。 我们正在努力使TypeScript易于采用,并让用户陷入成功的陷阱,而不是绝望的陷阱。 考虑到这一点,在尝试使用TypeScript并解决问题时,我经常问自己两个问题:a)这是有效的代码吗?b)客户是否打算尝试这样做?

大约在我输入(非TypeScript)版本在Babel中完成我想要的工作的时候,我认为可以肯定地说代码是有效的。 当React文档的安装页面显示如何使用CDN中的脚本标签来包含React时,我想很多人也会尝试这样做。 (FWIW:我花了更多的时间来记住要使用各种JS模块和加载器,因此并不是我不知道它们,我只是想以这种方式编写代码)。

如果TypeScript将不支持某些有效的代码编写模式,则我们应设法使其立即变得明显并引导人们正确使用(这在错误消息或简洁文档中是一个挑战)。 但就我个人而言,我不认为TypeScript不应该支持模式,因为我们认为它们不是“最佳实践”或“规范”。 如果代码有效,并且某些JavaScript开发人员可能想要编写代码,则TypeScript应该尝试支持它。 我们要求他们更改代码并重新配置其构建管道以使TypeScript正常工作的次数越多(如此处建议的那样,迁移我的琐碎应用程序),则开发人员的迁移就越少。

至于解决方案...这里只是吐口水,但也许“ lib”编译器选项已经有效地定义了整个项目中可用的API,它也可以采用@types/name格式值供库全局添加(甚至可能支持相对路径)。

我们正在努力使TypeScript易于采用,并让用户陷入成功的陷阱,而不是绝望的陷阱。

我认为我们正在尝试将用户引导到成功之地。 如果模块仅有条件地定义了全局,则您会无意间引导用户使用不存在的东西。 所以我看到了几种不同的选择:

  1. 创建一个增强的export as namespace foo构造,该构造仅在模块未导入时才可见。
  2. 不执行任何操作,并继续推动人们使用导入-在我看来,这或多或少都不错,因为无论如何我们已将错误消息合理地规定为规定性。
  3. 允许人们从任何地方使用UMD-老实说,我对这个想法不太了解。

@比尔蒂

重新输入您的shims.d.ts,如果您上了几篇文章,您会发现这就是我现在所做的(真主意也是如此);-)

抱歉,我错过了,非常好;)

我不认为TypeScript不应该支持模式,因为我们认为它们不是“最佳实践”或“规范”

我认为TypeScript在这里不是强制性的,我认为它正在按要求进行,告诉我我有一个错误。 许多库都有演示和教程,它们在其中将自己加载为全局变量,然后继续使用ES Module语法。 我认为这样做并不是最重要的公民,但这是另一种讨论。

也就是说,如果模块主要用作全局变量上的“感知糖”,那么它们的失败就在眼前,因为它们根本不是语法糖。 如果有的话,它们就是语法上的盐(也许是一种税收?),我们为了真正的代码隔离,免于脚本标记排序,显式依赖项声明,逃离全局名称空间地狱等好处而消费。 模块的语法不是符合人体工程学的,它充其量是冗长的,但是模块的语义才使它值得。

我认为,如果人们至少在.ts文件中使用TypeScript,我认为他们想获得强大的静态代码分析的好处。 Babel不会这样做,前提是React存在但不了解。 即使故意将ES模块指定为适合静态分析,也是如此。

@DanielRosenwasser

创建一个扩展的导出作为名称空间foo构造,该构造仅在模块未导入时才可见。

听起来这是解决此问题的最佳方法。

这是引起问题的另一种情况:

在我目前正在从事的项目中,我们将本地包含项(主要是出于历史原因)与npm模块混合在一起。 最后,所有内容都使用Rollup或Browserify加入,所以很好。

我使用了emojione项目中的.js文件,我只是将其复制到了代码库中。 后来我将它的类型声明添加到DefinitelyTyped中: https :

我之所以不使用npm模块,是因为npm模块还捆绑了数MB的sprite和PNG。 我只需要一个200KiB脚本。 带类型声明。

使用AngularJS时,解决方法是declare var angular: ng.IAngularStatic 。 但这不适用于名称空间,对吗?

@dbrgn您遇到了其他问题。 如果模块实际上是全局模块,则您的类型定义不正确。 它既不声明全局,也不声明UMD样式声明(这与UMD样式声明有关),实际上它仅声明纯ES模块。

如果模块表示全局模块,则不要在文件的顶层导出,这会使它成为模块。

使用AngularJS,解决方法是声明var angular:ng.IAngularStatic。 但这不适用于名称空间,对吗?

它适用于名称空间。

在设计会议上讨论的结果是,我们正在考虑始终允许UMD,并添加一个强制实施当前限制的标志。 该限制也将扩展为适用于从UMD全局访问类型。

经过更多考虑之后,我仍然认为更好的方法是创建一种新的声明类型。 该标志比新语法更难被发现,新语法仅需要声明文件作者编写一次。

现有代码和工具非常需要此功能。 在这样的情况下,直到Javascript停止快速播放并迷失于模块系统之前,我们需要灵活地使用已有的代码。 发出警告,但不要使构建失败。 我浪费了很多时间来处理使遗留代码在汇总和打字稿中发挥出色的作用。

啊。

我知道有很多人喜欢嘲笑Java,但是基本的Java模块至少可以工作。 罐子工作

我不必适应14种不同的临时模块标准,也不必尝试从源文件中编译js模块,其格式为当今的汇总/捆绑工具实际上会消耗而不会大便自己,并且还会生成模块格式可以很好地与Typescript导入/导出语句和第三方d.ts文件配合使用,以便TSC真正决定构建代码,而不是抱怨您要去的地方“仅使用DADAR导入,它将在全球范围内运行”。

shims.d.ts hack效果很好。 可是

对于使用Webpack的用户的临时解决方案https://github.com/Microsoft/TypeScript/issues/11108#issuecomment -285356313

externals webpack.config.js与所需的UMD全局变量加到

    externals: {
        'angular': 'angular',
        'jquery': 'jquery'
        "react": "React",
        "react-dom": "ReactDOM"
    }

我认为应该可以简化迁移现有代码库的过程。

我有一个使用requirejs实现的项目,其中jQuery包含在全局中,因为有些插件仅在被发现是全局时才扩展jQuery。

某些模块中的代码取决于该插件,如果将jQuery作为模块导入,则该插件将不可用。 为了使这项工作有效,我将必须修改所有插件才能与作为模块加载的jQuery一起使用,同时也将它们作为模块加载(猜测它们在哪里是必需的)。

此外,还有一些页面在不使用模块加载器的情况下使用javascript。 因此,插件应与全局变量和模块一起使用。

除jQuery外,还有其他脚本也有同样的问题,例如敲除等。 这使得迁移项目变得很困难。 或者,从现实的角度来看,这是不可行的。

当然,这不是最好的模式,我不会在新项目中使用它。 但是我不认为我是唯一一个有这个问题的人

为此,在tsconfig.json使用types有意义吗? 例如,没有设置types ,您将得到当前的隐式行为,而设置了types ,则是在说“这些都是全局的”,并且可以强制UMD命名空间全局显示。 无论如何,这就是今天存在的行为(减去全局力)。 这与引入新的globals选项相对。

我认为这是个好主意。 就我而言,有些脚本将UMD库用作全局脚本,而其他脚本则用作模块。 我可以用两种不同的tsconfig.json解决此问题,分别解决每种情况。 真的很直率。

@blakeembrey尽管使用types是有意义的,但由于它已经存在问题,所以我不太热衷于重载它。 例如, <reference types="package" />构造已经具有不支持"paths""package"必须引用@types的文件夹名称

这段对话后,我很难过。 是否对此有任何更新或计划的解决方案? 似乎这在场景中很有用,例如lodash是应用程序不可或缺的一部分,或者当更多的第三方库转换为更模块化的结构而不仅仅是依赖于窗口时。

是否有计划的方法来解决此问题,或者至少记录如何在当前可用版本中解决该问题?

@mochawich我在将React定义为外部对象而不使用declare global语法时遇到以下错误:

TS2686: 'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.

@cantux TypeScript无法读取Webpack配置。 如果您已在全球范围内提供React,则可以declare ,但是为什么不使用模块呢?

@aluanhaddad主要是因为我对导入调用所完成的工作感到困惑。 我摆弄一些,以下陈述正确吗?

导入模块时,我们将支付少量的请求费用。 这可以确保我们正在使用的内容在内存中可用,如果先前已请求过,则从缓存中加载模块,如果模块不存在,则将其获取。 如果您想绕开此请求,只需将其定义为global,而Typescript便盲目地相信它是可用的(如果您使用智能捆绑程序,则可以替换/删除import语句)。

如果这些是正确的,为了简洁起见,我们可以删除注释,因为thread是一个巨大的东西。

正如@codymullins所问的那样,有人可以总结此问题的当前解决方法吗? 我刚刚更新了lodash类型定义,但出现了许多TS2686错误。

我当前的解决方法是破解typedef文件,使其符合旧的工作标准,但是如果更多的typedef文件开始损坏,那将是不可行的。

我的情况如下:

  • 在我的单页应用程序中,我将许多库(包括lodash)导入到

尽管vscode将其突出显示为错误(即使它仍能正确完成补全),但上面提到的shim示例确实有效。

请访问模块中的UMD全局变量时请不要出错。 我正在处理的大型项目是在AngularJS中完成的,我们正在为应用程序使用Typescript,但是我们当然需要Typescript来了解@types/angularangular UMD全局和Angular类型。 您可能会认为,将"angular"types中的tsconfig.json types一样容易,但是,由于某种原因,事实并非如此,TSC向我尖叫。 我希望所有NPM软件包都是纯Typescript,但大多数都是纯JS,而且使用时间很长。 我真的不明白为什么当我们导入d.ts说存在UMD全局时,为什么TSC不能仅仅关闭。 这种情况比平常要多-我曾经从事的每个Typescript项目都至少使用一个JS库,我必须将其捆绑在一起并使用类型定义进行引用。

有什么更新吗?

我的用例:我正在开发一个庞大的现有代码库,该代码库大量使用CDN。 通用实用程序通过脚本标签跨多个页面导入(例如,剪贴板,lodash)。 我想引用这些全局变量,因为它们在页面上可用。 在不使用模块的情况下,使用相关源文件顶部的/// <reference type="$name" />可以很容易地进行打字稿的编译。 但是,这在尝试创建模块时停止工作。

线程中似乎提出了两种方法:

  1. 仅将/// <reference type="$name" />导入令牌导入当前文件的名称空间。

  2. tsconfig.json的编译器选项/配置变量(例如"globals""types"

我认为两种方法都很好。 虽然我同意@RyanCavanaugh对选项1的

相反,如果仅在该文件中可用,那么您将不得不在各处复制和粘贴参考指令,这确实很烦人。

我相信,由于当前的行为,您完全不能将模块与UMD全局变量一起使用。 有些解决方法总比没有好。

这个问题仍然存在吗? 如果是这样,当前的解决方法是什么?

如果您安装@types软件包,则那些未作为模块导入的软件包将作为全局变量提供。

例如,如果我npm install -D @types/underscore在项目的根目录中,那么我可以编写不从下划线导入任何内容的模块,但是全局_可用(请参见下文)。

types-ref

那是你所追求的吗?

@billti也许我误会了,但是您的榜样对我不起作用。

最少需要重现:

js / foo.ts:

// <reference types="js-cookie">

import { Bar } from "./bar";

const Foo = {
    set: function() {
        Cookies.set("foo", "bar");
    },
    get: function() {
        console.log(Cookies.get("foo"));
    }
};

window.onload = function() {
    console.log(Cookies);
}

js / bar.ts

const Bar = {
    x: 3
};

export { Bar };

package.json:

{
  "name": "foo",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "private": true,
  "devDependencies": {
    "@types/js-cookie": "^2.1.0",
    "typescript": "^2.7.1"
  },
  "dependencies": {
    "http-server": "^0.11.1"
  }
}

tsconfig.json

{
    "compilerOptions": {
        "module": "system"
    },
    "files": [
        "js/foo.ts"
    ]
}

错误讯息:

js / foo.ts(7,3):错误TS2686:“ Cookies”是指UMD全局变量,但当前文件是一个模块。 考虑添加导入。
js / foo.ts(10,15):错误TS2686:“ Cookies”是指UMD全局变量,但当前文件是一个模块。 考虑添加导入。
js / foo.ts(15,14):错误TS2686:“ Cookies”是指UMD全局变量,但当前文件是一个模块。 考虑添加导入。

您获得的行为取决于导入的模块是作为“正确的” UMD模块编写的(这是“ Cookies”的行为)还是“同时工作的双向”模块(这是lodash的编写方式) 。

人们正确编写描述对象在运行时如何工作的.d.ts文件的不一致,以及开发人员的经验不透明,这就是为什么我非常努力地朝着“删除UMD全局限制”的方向努力的原因。 我们可以选择退出--noStrictUMD将其置于--strict

我遇到的另一件事是与摩纳哥定制的AMD装载机打交道。 它们支持AMD行为的某些子集(插入巨大的眼球),但会踩踏全局的“需求”,因此很难使用适当的UMD模块,因为这些模块倾向于看到全局的“需求”,然后无法正确加载到全局中。摩纳哥模块装载机。 最后,您将UMD JS库放在Monaco加载程序的script标签之上,然后TS抱怨,因为您正在从模块访问全局变量(必须导入Monaco API)。

@RyanCavanaugh

我遇到的另一件事是与摩纳哥定制的AMD装载机打交道。 它们支持AMD行为的某些子集(插入巨大的眼球),但会踩踏全局的“需求”,因此很难使用适当的UMD模块,因为这些模块倾向于看到全局的“需求”,然后无法正确加载到摩纳哥模块装载机。

😁

他们有机会解决这个问题吗?

最近,我一直在思考模块的复杂性成本。 如此众多相互依赖,部分兼容的装载程序,打包程序,编译程序,程序包管理器和框架,实际上构成了极为复杂的授权复杂性。 (我确定您you都不需要提醒)。

作为开发人员,我们接受的工具链比5-6年前复杂了几个数量级,而复杂性的主要来源是模块。

如果我们放弃并开始以全局变量的形式加载这些UMD软件包,那到底是为了什么?

但是...人们只是这样做。 这真糟糕!

我的意思是,这个堆栈溢出答案有61 s,并且在过去半年中,它一直在为99%的软件包提出所有错误提示。 (由于今天早上提供了一些反馈,作者对它进行了更新,将模块作为UMD依赖项的_option_提及)

这不能让所有这些都回溯到全局变量!

但是...人们只是这样做。 这真糟糕!

问题在于,JS模块的构思和实现非常差,因此回到使用全局变量会变得更好,更容易。 如果从一开始就对模块进行了正确的设计和实施...

我们将模块和UMD全局变量进行混搭,因为将依赖项加载为具有与各种加载器不同程度的兼容性的模块非常麻烦,并且某些加载器不支持直接依赖项捆绑,而您必须使用带有分钟运行。

此“功能”仅表示即使我们确实使用UMD模块,也不会使用官方的UMD模块支持。 我们只是从.d.ts文件导出为全局文件,然后手动使用我们自己的名称相同的模块来重新导出所有内容。

这事有进一步更新吗? 我真的希望选项2可以工作:

允许某些语法或配置说“此UMD全局实际上在全局可用”

同样有太多老式风格的js库也无济于事
在那里,将它们全部重写是毫无疑问的。 只是一个坚实的
配置让汇总正确处理它们会浪费我的时间。

人们嘲笑“ Enterprise Java”,但普通的Java附带了可行的
1.0版的模块系统。 不是完美的,但不是一团糟。

因为它是“允许UMD样式全局变量”,所以绝对应该是一个选择。

于2018年4月2日星期一1:40 AM Kagami Sascha Rosylight <
[email protected]>写道:

我不得不解决:

/ *模块:es2015 * /
// // js-yaml支持UMD但不支持ES2015模块!

-
您收到此邮件是因为您发表了评论。
直接回复此电子邮件,在GitHub上查看
https://github.com/Microsoft/TypeScript/issues/10178#issuecomment-377885832
或使线程静音
https://github.com/notifications/unsubscribe-auth/AA50ljAD33sr09EGFVAsURbu1x75X-lOks5tkeQCgaJpZM4Jd8jX

>

丹尼尔·乔伊斯

温顺者将继承大地,因为勇者将在星空之中。

我在Angular项目中使用three.js。 我将其导入为

import * as THREE from "three";

import {Vector3} from "three";也可以按预期工作。

安装three@types/three npm软件包后,一切正常。 在幕后,我想这是使用three/build/three.module.js@types/three/index.d.ts文件使用符号export as namespace THREE ,我对此并不完全满意,但是它可以工作。

在这种特殊情况下,问题是在three.js系统中还有另一个相关文件称为OrbitControls.js (基本上,它允许您用手指或鼠标旋转3d图像)。 问题是,尽管此函数是three.js发行版的半官方一部分,但它是在examples树中找到的纯旧JS文件,并将其直接放置在THREE属性中并直接使用它期望在window.THREE上找到的其他API。 因此,即使我“需要”文件

require("three/examples/js/controls/OrbitControls.js");

它找不到window.THREE来穿上衣服或访问​​它使用的三个系统的其他部分。 我可以使用angular.json的Angular scripts属性直接包含整个库(大致相当于老式的<script>标签),但是如果我没记错的话,将两次加载该库。

为了避免这种情况,我删除了import * as THREE from "three";语句,嘿,它仍然可以解析foo: THREE.Vector3类的类型,但臭名昭著的引用诸如new THREE.Vector3()

“三个”是指UMD全局文件,但当前文件是一个模块。 考虑添加导入。 [2686]

在这一点上,我正在考虑只需要抓取OrbitControls.js文件并对其进行ES6-ify和/或TS-ify处理,这似乎已经超过一个人在像`orbit-controls-es6之类的东西的形式,所以也许我应该简化我的生活并使用它,即使我讨厌这样把生活放在别人的手中。

半不相关的说明是,即使代码本身不在three模块本身中, @types/three也会为OrbitControls @types/three定义类型。 但是,我不知道如何将所有定义类型为OrbitControls与任何内容相关联-我想声明上述orbit-controls-es6的默认导出为这种类型,但是如何做到这一点使我难以理解。

我最后想到的解决方案是:

import * as THREE from "three";
Object.defineProperty(window, "THREE", {get() { return THREE; }});
require("three/examples/js/controls/OrbitControls.js");

尽管我对为什么有些困惑,但它确实有效。 所需文件中有一行,例如

THREE.OrbitControls = funtion() { };

看起来最终将分配给import * as THREE from "three";语句产生的三个“命名空间”,这不应该,应该吗?

@RyanCavanaugh要求我在此处复制#26223的反馈:

我正在维护一个相当大的TypeScript代码库(Google的内部monorepo),其中包含一些人们依赖的类型明确的库。 最初,用户将$$$ angular类的库的全局类型作为依赖,然后将.d.ts转换为外部模块。 然后,我们迁移了代码库以使用模块和显式导入。 我们实际上希望export as namespace d UMD全局变量始终需要显式导入才能使用符号(对于类型和值引用),并且甚至在迁移时都不会回头(嘘)。

允许非导入使用代码通常对我们来说是有问题的:

  • 这意味着代码依赖于全局“背景”类型定义,从而使代码更难阅读(尤其是在回购浏览器或不带符号的代码查看中)。

  • 它掩盖了代码依赖性

  • 它规避了我们在bazel中实施的“必须对所有导入具有明确的构建级别依赖性”约束,也就是“严格的部门”。

    在大型代码库中,您的代码必须具有显式依赖关系,否则存储库将变得无法管理。 现在,如果您有一个依赖A -> B -> C和全局类型,只需$$$ B就依赖于C编译代码A C 。 如果稍后B删除其依赖性,则会破坏A ,这意味着更改对存储库产生意外的连锁反应,从而违反了代码隔离。

  • 这会导致代码不一致地使用一个前缀输入值并将模块的类型与另一个前缀一起使用(特别是对于AngularJS,ng与angular)

我们可以通过删除供应商提供的DefinitelyTyped副本中的export as namespace语句来解决此问题,但至少对我们而言,此功能有点不利于代码可维护性和我们的工程目标。 我认为这些问题在像Google这样的monorepo情况下更为明显,但通常也适用于较小的代码库。

您发布的观点对我们的情况绝对不重要。 由于环境超出了我们的控制范围,我们被迫使用AMD模块实现自己的代码,并向我们的依赖项提供UMD模块(但我总结为JS模块在概念和实现上都存在严重缺陷)。 此功能将使我们大大简化生活。

也许通过TS3,我们可以弄清楚如何避免这种情况,但是即使我们做到了,也可能至少需要两年时间才能完成所有必要的更改,因此这对于我们来说仍然是非常有用的功能。

悬而未决的问题:一个“允许访问所有UMD模块”的全局标志是否足够,还是人们真的需要对错误进行逐模块控制吗?

投票❤️表示“仅一面旗帜”
投票给“需要按模块控制”

我也在考虑tsconfig.json"types"选项中是否存在显式列表是否也不允许模块中使用UMD。 它确实表示该类型是故意存在的。 (尽管显然不能排除您忘记导入的错误)​​。

或类似地,使用/// <reference types="..." />构造应允许UMD在其使用的模块中使用该程序包(即提到的“每个模块控件”)。

@RyanCavanaugh是否也将有一个标志地址#26233?

26233被认为已完全按预期工作; 从模块访问UMD全局类型的一侧是无害的

我不确定它是否“合法无害”。 以@types/jquery为例。 $jQuery映射到JQueryStatic接口并作为常量导出。 结果,所有模块都可以访问$jQuery而无需导入。 我希望我可以禁用它。

@RyanCavanaugh是的,在某种意义上说TS发射不会受到它的影响是无害的。 如果您想对每个库可以访问的@types细粒度的控制,这是有问题的-它正在将至少看起来像模块范围的类型变成全局类型。 即使发出不受影响,扩大访问范围也可能是一个问题。

实际上,在jQuery的情况下,发射会受到影响。 $()在不导入的模块中发出。

接受PR的新标志,该标志允许从所有模块访问UMD全局变量。

在实现方面,这非常简单...但是我们确实需要命名。 在建议审核会议上,我们踢出了12个可怕的名字,并讨厌所有这些名字,因此,所有人都需要提出可口的东西。 请暂停。

我们在建议审核会议上踢了十几个可怕的名字,并讨厌所有这些人

那是什么

因此,由所有人来提出一些可口的东西。

也许umdUseGlobal

我建议使用importAllNamespaces作为UMD全局标志的名称,因为UMD全局变量通常是export as namespace

@RyanCavanaugh团队是否讨论过类型问题?

@FranklinWhale是的。

@saschanaz已经问过这个了,但我也很好奇... @RyanCavanaugh您还记得讨论过哪些可怕的名字吗?

我认为连锁店是这样的

  • allowUmdGlobalAccessFromModules -最精确但很长
  • assumeGlobalUmd -嗯
  • allowModuleUmdGlobals -“ globals” ??
  • umdAlwaysGlobal -🤢
  • allowUmdGlobals -但我已经可以了吗?
  • allowUmdGlobalAccess -跳过模块部分,但可能没人在乎吗?

如果被迫我会选择最后一个

谢谢!

我最喜欢allowUmdGlobalAccessFromModules ,因为尽管它很长,但它的精确度使其最容易记住。 我会想:“允许从模块访问UMD全局变量的选项是什么?是的,当然是allowUmdGlobalAccessFromModules !”

使用前缀“ allow”匹配其他选项的命名约定,这很好。

另外...还有大约其他时间:)

allowUmdGlobalAccessFromModules :31个字符

allowSyntheticDefaultImports :28个字符
strictPropertyInitialization :28个字符
suppressExcessPropertyErrors :28个字符
suppressImplicitAnyIndexErrors :30个字符
forceConsistentCasingInFileNames :32个字符

当前的解决方法是什么? 我过去一个小时一直在搜寻,找不到任何可行的解决方案。
我宁愿不要转换为“ any”或降级为有效的Typescript版本,但是找不到其他选择。
在某个地方有一个具有编译器标记的实验性内部版本可以解决此问题吗?
(顺便说一句,“ allowUmdGlobalAccessFromModules”是一个很好的名字;这不像我们一天要输入50次:-))

我们使用的是tsc 3.2.2,顶部的HTML文件中静态包含lodash; 与require.js; 从最新的DefinitelyTyped获得的d.ts; 无法编译的示例代码:

/// <reference path="..." />

class Example<T extends IThingWithTitle<T>> {

    public test = (arg : T[]) : void => {
        _.sortBy(arg, (el : T) => { return el.title; }); // TS2686: '_' refers to a UMD global, but the current file is a module. Consider adding an import instead.
    };

}

export = Example;

(请不要告诉我我需要颠倒这个项目,我知道我们在某些方面落后了)

更新:((window)._)/ * FIXME https://github.com/Microsoft/TypeScript/issues/10178 * /。sortBy(...)可以工作但是亲爱的Lord,它很丑陋:-P

@Gilead ,此评论的解决方案https :

在这方面有什么进展吗? 我遇到一种情况,其中提到的解决方法似乎不起作用(使用[email protected] ),因为我遇到了此问题。


首先,我尝试了这个:

import 'firebase';

declare global {
  const firebase;
}

这将为全局firebase隐式地赋予类型any ,然后对其应用名称空间(具有相同的名称)。 首先,这似乎可行,因为它显示了firebase所有顶级键的正确工具提示/智能提示。

但是它实际上并不起作用(我想是因为它随后切换为使用any ,这可能是一个错误吗?):


因此,我尝试了此处提到的解决方法,但没有成功(尽管它对其他人也有效):

import _firebase from 'firebase'; // same with = require('firebase') 

declare global {
  const firebase: typeof _firebase;
}

=> 'firebase'在其自己的类型注释中直接或间接引用。
(即使命名空间是别名?)


我也试过

import * as _firebase from 'firebase';

declare global {
  const firebase: typeof _firebase;
}

=>导入别名'_firebase'的循环定义。
(也许是因为export = firebase; export as namespace firebase;在其定义?)


最后,如果我只是执行import 'firebase' ,我会回到

“ firebase”是指UMD全局文件,但当前文件是一个模块。 [2686]


如果有人对此有解决方案,将不胜感激。 否则,到目前为止所提到的解决此问题的任何建议对我来说似乎都很好(标志,三斜杠引用,tsconfig中的types ,具有globalexternal对象在tsconfig中)。

Re @aluanhaddad的评论

这不能让所有这些都回溯到全局变量!

我并不想尝试回到全局变量,我只是想让几个沉重的依赖项与我的应用程序捆绑分开加载,同时仍将模块用于其他所有项目,因为它带来了一些好处:可以正确地缓存依赖项因为它们的更新频率不如我的应用程序捆绑包; 我的捆绑包大小没有爆炸(有时是因为捆绑程序代码拆分多次包含相同的依赖项),这意味着我的应用程序用户下载量减少了; 在开发期间重建捆绑包的速度更快。

这确实是一个非常容易添加的功能。 对于社区成员来说,这很好。

@RyanCavanaugh我调查了一下,弄清楚了我必须将选项添加到compiler/types.ts并修改compiler/checker.ts的umd全局支票,我想我需要将其添加到compiler/commandLineParser.ts ......但是我认为要花很长时间才能完成它,因为我根本不熟悉源代码(例如如何在不破坏i18n的情况下为CLI标志添加描述) 。 现在,我将等待其他知道来源的人继续使用它。

@simonhaenisch可以在非模块声明文件中声明它,并且为了避免循环引用,可以在另一个UMD模块声明中将其重新导出。 最初的firebase被声明为名称空间,这对我们来说是幸运的,它将增加我们的声明,不会引起错误。

// umd.d.ts
import firebase = require("firebase");
export import firebase = firebase;
export as namespace UMD;

// global.d.ts
declare const firebase: typeof UMD.firebase;

不幸的是,我们声明的是值,而不是名称空间,因此您无法执行let x: firebase.SomeInterface ,对名称空间进行别名的唯一方法是声明导入,但不能使用declare import firebase = UMD.firebase; ,因为名称空间不会扩充它。 当然,我们只能对使用intype的namspace使用不同的名称,但这会引起混淆,我宁愿删除上面讨论的其余代码,在运行时将其分配给全局值,使导入别名真正起作用。

与前面的评论类似,我们正在延迟加载hls.js(UMD)和引用类型,因此:

hls.d.ts

import * as Hls from 'hls.js';
declare global {
    const Hls: typeof Hls;
}

在使用延迟加载的UMD模块的.ts文件中:

/// <reference path="hls.d.ts" />
// now use it
if(Hls.isSupported()){
 ...
} 

在Typescript> = 3.0.1和3.4.1中测试。

基本原理是浏览器对动态模块导入的支持不完整。

@MatthiasHild可以在没有/// <reference path="hls.d.ts" />评论的情况下完成吗?

编辑,是的,可以的,只要声明位于包含的.d.ts文件中,而该文件与另一个.ts文件的名称不同,则基于此SO问题(这就是让我知道以及为什么我问)。

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

相关问题

MartynasZilinskas picture MartynasZilinskas  ·  3评论

jbondc picture jbondc  ·  3评论

Antony-Jones picture Antony-Jones  ·  3评论

seanzer picture seanzer  ·  3评论

manekinekko picture manekinekko  ·  3评论