Typescript: 支持在导入时查找 node_modules 下的模块

创建于 2014-07-25  ·  138评论  ·  资料来源: microsoft/TypeScript

2015 年 11 月 5 日更新

从至少 1.8 版本开始,目前在 typescript 中实现了下面请求的功能,但有一个主要区别:

没有typescript.maintypescript.definition属性,只有一个typings属性可以指向d.ts文件或普通.ts文件。

如果你正在开发一个只在本地使用的模块,你可以让typings指向一个.ts文件,但是如果你打算发布这个模块,建议让它指向d.ts文件。 这是因为您不希望您的模块使用者重新编译您的模块文件,而只是使用它的类型。

我在这里设置了一个使用它的示例:
https://github.com/chanon/typescript_module_example

这里有一个文档页面,其中包含更多信息:
http://www.typescriptlang.org/docs/handbook/typings-for-npm-packages.html

感谢 TypeScript 开发人员和所有贡献者。

原始问题/功能请求如下


动机

与在 JavaScript 中重用 npm 模块相比,在 TypeScript 中重用 typescript 模块要困难得多。

如果 typescript 编译器足够聪明,可以查看 node_modules 文件夹和 package.json 文件,那将是有益的。

原因是使用 TypeScript 的 npm 模块开发人员可能能够通过 npm 本身开始编写和分发模块。 TypeScript 将能够搭载 npm 的基础设施和广泛的支持。

node_modules 的示例

如果我们有:

./node_modules/concator/index.ts
./myApp.ts

在 index.ts 我们有:

export function concat(param1: string, param2:string): string {
      return param1 + ' ' + param2;
}

在 myApp.ts 中:

import concator = require('concator');  // loads the module from node_modules
var result = concator.concat('I like', 'this.');
var wontWork = concator.concat('this will fail');  // compile error, concat needs 2 params

所以基本上,编译器足够聪明,可以在 node_modules 中找到模块,并自动使用 typescript 版本(index.ts)。

那么当代码编译成 JavaScript 时,自然会使用 JavaScript 版本。

将文件夹作为模块导入

一个更基本的案例是支持 Node.js 流行的http://nodejs.org/api/modules.html#modules_folders_as_modules规则,如下面的@vvakame和封闭(半重复)#207 问题所建议的那样。

package.json 中的 typescript.main

可以添加到 package.json 文件以指定 TypeScript npm 模块的主 .ts 文件在哪里。 这类似于main键,它指定主 JavaScript / .js 文件的位置,但用于 TypeScript。

例如 package.json 用于名为“myModule”的 npm 模块,位于node_modules/myModule/package.json

{
     "main": "./dist/index.js",
     "typescript": {
          "main": "./src/index.ts"
     }
}

在此示例中, node_modules/myModule/src/index.ts将是主要的 TypeScript 文件,执行import myModule = require("myModule");将导入node_modules/myModule/src/index.ts文件。

对于在 JavaScript 文件中编写var myModule = require("myModule");的 JavaScript 编码器,require 将照常加载node_modules/myModule/dist/index.js文件。

从这个例子中可以看出,TypeScript src 位于node_modules/module-name/src文件夹中,编译后的 JS 文件将位于node_modules/module-name/dist中。

这样的东西可能是 TypeScript npm 模块的(半)标准,以便 TypeScript 源代码与编译后的 JavaScript 输出完全分离。

下面@vvakame建议的一个更基本的情况是支持流行的 Node.js 规则http://nodejs.org/api/modules.html#modules_folders_as_module

package.json 中的 typescript.definition

非 TypeScript(纯 JavaScript)npm 模块的 package.json 的另一个可能键可能是typescript.definition 。 这将指向一个为 npm 模块的 TypeScript 用户定义模块的 .d.ts 文件。

所以一个
import $ = require('jquery');
将自动读取在 jQuery 的 package.json 中typescript.definition键中定义的jquery.d.ts文件,并使$成为正确的类型。

示例格式:

{
     "main": "./dist/index.js",
     "typescript": {
          "definition": "./index.d.ts"
     }
}

(这种格式已经被 tsd 使用,@ Bartvds在下面解释。)

然后我们的 TypeScript 编码人员只需要尝试让尽可能多的非 TypeScript npm 模块维护者来合并我们的拉取请求,这些请求包含我们的 .d.ts 文件和 package.json typescript.definition键。

如果我们成功了,那么 TypeScript 编码员的生活将是幸福的……不再单独管理 DefinedTyped .d.ts 文件。 只需 npm install 即可获得 TypeScript 定义! 自动更新安装的模块版本。

福利清单

我们从这一切中得到的是

  • npm 模块可以用 TypeScript 编写。
  • TypeScript 和 JavaScript 编码人员都可以使用这些模块
  • 使用 TypeScript 的模块用户可以获得静态类型的好处,而模块编码器(或用户)不必使用通常的方法来编写(或生成)单独的 .d.ts 文件(所以当模块的源代码已经在 TypeScript 中时)我们不必维护另一个 .d.ts 文件)
  • 有一种方法可以使用每个人都已经熟悉的 npm 轻松重用和分发 TypeScript 模块
  • 它可以帮助促进 TypeScript 本身的使用,因为使用 TypeScript 编写的 npm 模块的 JavaScript 编码人员可能会看到 TypeScript 源代码有多好/更好,并尝试切换到它。 或者他们可能想为模块做出贡献,并且由于源代码在 TypeScript 中,他们必须学习它,这可能会导致他们喜欢它并决定在自己的项目中使用它。
  • 基本上,它将通过可能产生的所有代码共享来帮助发展 TypeScript 社区。 现在每个人的 TypeScript 代码大多是他们自己的 / 在他们自己的孤岛中。 这实际上可能是一件极其重要的事情,因为没有适当/简单的方法来共享/分发/重用模块可能会限制语言的发展。 [1]
  • 在内部项目中重用模块会少很多麻烦,并且会减少对所有这些 .d.ts 文件的相对路径和模块要求中的相对路径以及一般 .d.ts 东西的需求。 (现在在编写 TypeScript 代码时,我发现我必须学会擅长计数 ../../ s)
  • 这种方法还支持 Browserify/Webpack,因为 Browserify/Webpack 也可以在 node_modules 下查看,因此这允许服务器和浏览器的轻松可重用/npm 分布式 TypeScript 模块
  • 非 TypeScript npm 模块可以通过typescript.definition键添加轻松地在 TypeScript 中使用类型信息。 这允许将 .d.ts 定义文件与 npm 模块打包在一起,以便更新 npm 模块将自动更新其 .d.ts 定义文件。 这消除了手动更新 .d.ts 文件的需要。
  • 通过typescript.definition使用定义文件更简单,因为它只是一个import moduleName = require("moduleName")语句,不需要单独的///<reference ...
  • 通过typescript.definition使用定义文件还应该允许在同一代码库中使用不同版本的模块,而不会发生类型名称冲突。

详细提案

@Nemo157写了一份非常详细的提案,说明所有这些应该如何工作:

提议的 TypeScript 需要解析语义
https://gist.github.com/Nemo157/f20064a282ee620f3877

提案中的一个补充是使用/typings文件夹,这些文件夹可以保存定义文件,这些定义文件可以由诸如tsd等工具自动管理,用于 JavaScript npm 模块,这些模块不会在其存储库中包含定义文件.

最后的支持事实

由于 TypeScript 编译为主要在两个地方运行的 JavaScript:node.js 和浏览器,由于 npm 和 Browserify/Webpack,支持 node_modules 对这两个地方(实际上所有使用 TypeScript 的地方)都有好处。

IE。 当所有 TypeScript 用户已经使用 node_modules 来处理他们所有 JavaScript 代码的 75%-100% 时,没有理由提出不同的方案。 (为可能的 RequireJS 用户取出 25%。)

脚注

[1] - 顺便说一句,我看到微软有一个 NuGet 包管理器(?)可以分发打字稿模块(?),但是来自一个以 node.js 为重点(非 .NET 重点)的背景,我不认为 N​​uGet 成为在以微软为中心的商店之外广泛使用,特别是因为 npm 是 node.js 的_the_ 标准,也是客户端 JavaScript 的领先标准。 如果我不使用 TypeScript,我永远不会听说过 NuGet。 一般的 node.js / 客户端 JavaScript 编码器可能更喜欢使用 npm,他们已经使用相同的工具,而不是必须使用 Microsoft 特定的东西,例如 NuGet。 (我实际上对 NuGet 一无所知,所以我在这里所说的可能并不重要。)

Committed Suggestion

最有用的评论

无论如何,编译器目前声称它找不到模块,而实际上它只是拒绝导入没有类型定义的模块。 这不是很有帮助,是吗?

所有138条评论

[在上述问题描述中移至package.json 中的 typescript.main ]

:+1:
我认为http://nodejs.org/api/modules.html#modules_folders_as_modules是 Node.js 非常流行的规则。

另一个例子。

./test/index.ts

export function hello() { return "Hello, world"; }

./main.ts

import test = require("./test/");
console.log(test.hello()); // print "Hello, world"

[移至上述问题描述中 package.json 中的 typescript.definition ]

一种解决方案是使用更好的工具来解决它。 例如import foo = require('foo')提示您寻找一些本地 node_module+package.json(foo 是一个 ts 项目)或 DT 定义(foo 是一个库作者不想维护 ts def 的 js 项目) .

供参考; 我实际上是在 TSD 中测试类似的东西; 一种公开和链接捆绑在 npm(或 bower)包中的定义的方法。

它在我的 0.6 开发版本中,它通过向 package.json(或 bower.json)添加一个typescript元素和一个子元素definition (一个子元素,因为可能是一个一天也会有source ,或其他)。

{
    ...
    "main": "./index.js",
    "typescript": {
        "definition": "./foo.d.ts"
    }
    ...
},

然后你可以在 TSD 上运行一个命令,目前是tsd link ,它将扫描 node_modules(或 bower 或其他)中的所有 package.json 文件,如果已定义,则找到该属性并将对它的引用添加到中央tsd.d.ts捆绑在您的项目中。

示例:在此处定义并此处使用

@Bartvds这很好。 可能是在 npm 包中包含 .d.ts 的第一步。 我喜欢“typescript”中带有“definition”的 package.json 结构,它更有条理。

如果 TypeScript 编译器本身可以自动读取它,那就太酷了。

标记它以供讨论——更智能的外部模块解析是我们需要讨论的内容,以了解这里的各种场景。 这是之前提出的,我们在 1.0 中做了一些调整。

还讨论#207 - 在“索引”下查看

:+1:

如果我们使用声明生成, @chanon tsMain不是必需的。

是的,即使对于浏览器,这个问题也非常重要——许多项目使用 browserify/packify,因此与节点兼容的目录布局

:+1:

在 codeplex 存储库中,该领域已经有一些工作,leebyron 的一个 pull request 和 kayahr 的一个 pull request

我一直想尝试将其中一个移植到新的存储库,但似乎相关代码已经重新安排了很多。

我已经写了一个关于如何将它添加到 TypeScript 编译器的提案。 这还包括查看typings目录,因为这在使用不会维护自己的打字稿定义的 javascript 模块时很重要。 不幸的是,使用tsd使这一切透明地工作仍然需要一些工作,因为/// <reference d 文件作为环境外部声明的要求会使事情变得有点麻烦。 我将看看在一个分支上实现它并提供一些使用它的示例模块。

看起来很明智。 这会涵盖具有冲突类型依赖关系的模块吗? 我的意思是如果一个应用程序导入了 A 和 B,并且 B 也依赖于 A。如果有两个外部声明副本,则很容易陷入重复类型错误。

它不应该,它们应该解析为不同的外部模块名称,因此在不同的模块中声明具有相同名称的独立类型。 然后,结构类型检查应该允许不同的模块进行交互而不会出现问题。

这是当前tsd定义试图解决的主要问题之一,通过定义外部环境模块,它们无法处理具有相同名称但类型略有不同的库的多个版本.

@joewood不同版本的 A 之间也会有麻烦

├── [email protected]
└── [email protected]
    └── [email protected]

他们无法处理具有相同名称但类型略有不同的库的多个版本。

有兴趣听到相同的解决方案。 TypeScript 具有用于环境声明的global级别变量范围

即使版本匹配,目前我看到两个不同但匹配的环境声明文件导致重复符号错误的问题。 本地定义的/// reference指令需要以某种方式解析回单个文件。 要么,要么类型标识需要包含路径,并且结构类型将处理版本不匹配等问题。

@joewood是的,这就是我希望在大多数情况下通过更改require以加载正确的定义来解决的问题,尽可能避免/// reference 。 外部模块将通过它们的绝对路径而不是环境声明来标识,因此应该可以从不同路径和不同版本加载多个相同名称。

是的,这就是我希望在大多数情况下通过更改 require 以加载正确的定义来解决的问题,尽可能避免 /// 引用。

:+1:

:+1: 感谢@Nemo157和大家的讨论。

我更新了最重要的问题文本以合并 package.json 添加并链接到@Nemo157的提案。

:+1:

我已经完成了我的提议的初步实施,并创建了一组示例模块,以表明制作模块的所有不同方法都可以工作。 包括在不同的子模块中使用同一个库的不同版本。

:+1:

文件夹作为带有 index.ts 的模块:+1:

:+1:

:+1:

:+1:

:+1:

:+1: :+1: :+1:

:+1: :+1: :+1: :+1:

评论这件事回到我们的雷达上。

@Bartvds的建议让我取得了很好的成功:

然后您可以在 TSD(当前为 tsd 链接)上运行命令,它将扫描 node_modules(或 bower 或其他)中的所有 package.json 文件,找到该属性(如果已定义)并将对它的引用添加到中央 tsd.d.ts 包在你的项目中。

所以一定要考虑在package.json中支持typescript ,因为这就是社区的发展方式。

如果 TypeScript 要在 node.js 社区中广泛使用,我们肯定需要 tsc 了解node_modules文件夹。 我目前使用符号链接到属于我的开发项目的模块(使用 npm-workspace),目前我有两个选择:

  • 直接从node_modules导入,看起来不对: import Foo = require("node_modules/mymodule/Foo");
  • 让 tsc 生成声明文件,然后使用手动维护的模块声明文件将它们拼接在一起,该文件切换字符串模块名称

tsc 是否可以在导入声明中检查外部模块名称,然后在路径解析期间也以与 node.js 运行时相同的方式应用node_modules ? 编译器选项也可以打开或关闭它。

:+1: :+1: :+1:

tsc 是否可以在导入声明中检查外部模块名称,然后在路径解析期间也以与 node.js 运行时相同的方式应用 node_modules

这就是它_应该_的样子。 当前仅查找目录树以查找具有所述名称的文件的方式与 JS 执行上下文没有任何相似之处。

这已经在我的名单上一段时间了。 将尝试在下一个版本中得到这个。

我们 ( Booktrack ) 使用 TypeScript 进行所有 Web 开发,并且我们面临着与 node.js 开发人员类似的挑战。 我发言是因为 web 开发人员的声音似乎没有节点开发人员的声音那么响亮。

解决方案(按照对我们最不利到对我们最有利的顺序):

  1. 通过硬编码查找node_modules来解析顶级外部模块名称
  2. 无法控制顶级外部模块名称的解析
  3. 一个编译器标志,用于控制顶级外部模块名称到node_modules目录的可选解析
  4. 编译器“模块搜索路径”参数,用于控制顶级外部模块名称到给定路径的可选解析
  5. 一个编译器“模块解析器”参数,为编译器提供一个 JS 插件,该插件将解析所有(不仅仅是顶级)外部模块名称
  6. 适当挂钩到语言服务,以便外部模块名称解析是可控的

选项1很糟糕,我宁愿选项2。

选项 5 和 6 将允许使用 import 语句的奇异用法,例如在使用 requirejs 或 webpack 插件时发现的那些。 基本思想:编译器将所有外部模块名称查找(不仅仅是顶级)委托给第三方(插件或回调)。 委托给定正在编译的模块的路径和外部模块名称,将文件系统路径或给定模块名称的类型信息返回给编译器。

如果实施了选项 4,我会很高兴,如果实施了选项 6,我会很高兴,如果实施了选项 4 和选项 6,我会欣喜若狂。

您的模块搜索根目录的 --basePath 怎么样,所有模块导入都将相对于该路径进行查找。 这仅适用于 AMD。

我认为节点问题要简单得多,我们只需要实现它:)

请注意,我们实际上很久以前就从@Nemo157那里得到了一个实现(和测试) https://github.com/Microsoft/TypeScript/issues/247#issuecomment -57422329

同意 @mark-buer 和@mhegazy ,选项 4 搜索路径应该是一个简单的修复。 语言服务(选项 6)中更精细的解决方案肯定需要长期。

_值得添加_:仅此一项并不能解决 npm 中的 TypeScript 重用问题,因为常见类型引用 #1125 的重复符号问题。 我们真的需要避免///通过使用.tsconfig文件并按照 #17 中的建议通过将包捆绑到单个外部类型模块中来正确修复此问题

让 typescript 编译器识别 node_modules 文件夹的另一票。

对于 CommonJS 模块,typescript 理想情况下应该遵循与 require.resolve 相同的文件名解析(如本页面上的伪代码所述: http://nodejs.org/api/modules.html#modules_all_together。

此功能对于允许将 typescript 编译器集成到其他节点打包程序(例如 browserify)中至关重要。 @Nemo157的补丁做得很好,但它不允许使用现有的打包程序进行简单的测试,因为大多数已经转移到更高版本的打字稿并且不再与他的代码兼容

node_modules目录必须相对于源在哪里?

Atom-TypeScript 现在支持typescript.definition开箱即用。 详细信息: https ://github.com/Microsoft/TypeScript/issues/2829

您还可以使用 atom-typescript 轻松创建此类包 :rose: 再次参见 #2829

局限性

如果您共享一个不依赖任何外部库的包,它将完美无缺地工作。 如果是这样,那么我们需要一个模块冲突解决算法。

我愿意接受对此案的建议,但我的计划是:

  • 我们在阅读这样的d.ts时做了一些聪明的事情,没有给 TypeScript 外部的reference注释(例如 node.d.ts 这里 https://github.com/TypeStrong/atom-typescript-examples /blob/master/node/node_modules/example-typescript-b/definition/sample-bdts) 而是指向我们自己的.d.ts ,如果我们的 Typings 中有它的话。

这是否会在 2.0 中保持正常? 对于 Node.js 的采用来说,这似乎是一个重要的特性,现在这很痛苦,即使是使用 distinctlyTyped 之类的。

对于 Node.js 的采用来说,这似乎是一个重要的特性,现在这很痛苦,即使是使用 distinctlyTyped 之类的。

@LPGhatguy请参阅https://github.com/Microsoft/TypeScript/issues/2338。 @vladima正在研究它,但不认为它被分配了任何里程碑:玫瑰:

@LPGhatguy我们正试图在下一个版本中实现这一点。 希望不久。

@mhegazy您的意思是1.6? 那将是真棒!

这与#2338有关吗?

对于 TypeScript 1.6 是肯定的(至少这是我们正在尝试做的),对于 #2338 是肯定的; 围绕此问题还有一些其他更改和问题,包括 #3147 和 #4154

那么,为什么将它标记为 TypeScript 2.0 作为里程碑呢?

谢谢@heycalmdown ,删除了里程碑。 我们通常不会将里程碑标记为建议。 希望我们能在一周左右的时间内对这个问题进行更新。 敬请关注。

我想要求 ES6 模块支持也遵循这一点(我认为它会?)

打字机

import mylib = require('mylib');
mylib.foo(mylib.bar);

行为应该与

import { foo, bar } from 'mylib';
foo(bar);

我想要求 ES6 模块支持也遵循这一点(我认为它会?

@DavidSouther _自动地_就是这种情况。 两者之间的查找是一致的 :rose:

这由#4154 处理。

如果我每晚使用,从明天开始我应该期望导入节点模块“正常工作”?

问题:
我认为将文件命名为常见约定
commonJS 中的“索引”和
AMD 中的“主要”
对于短路径。
在上面的问题中,作者导入
/node_modules/concator/index.ts
import concator = require('concator');

请原谅我 - 我目前正在弄清楚现在是否还支持索引分辨率以及 AMD 中的 index.ts 或 main.ts 需要分辨率如何?
我也在 ping @basarat以了解它是否/将被https://github.com/TypeStrong/atom-typescript支持,因为现在它不允许我需要上面提到的任何 index.ts。

@sebilasse ,如果您今天使用typescript@next ,这应该适用于--module commonjs

import concator = require('concator'); // resolves to node_modules/concator/index.ts

与以前的版本相比,AMD 分辨率的方式没有变化。

@DavidSouther今天应该在typescript@next中。 试一试,让我们知道它的票价。

@mhegazy @basarat :+1:好的。 但是您如何看待 main.ts 或 index.ts 的 AMD 分辨率 - 将来不应该保持一致吗?

@sebilasse ,对于 AMD,我认为我们需要按照@vladima在 #2338 中指出的那样做一些事情(部分:RequireJS/ES6 模块加载器)。

非常好!! \o/

@mhegazy仍然没有像我预期的那样工作。

我将https://github.com/DavidSouther/typescript-example-using-node与我的“理想”用例放在一起。 tslib 是一个导出单个函数的简单库。 tsclient 依赖于 tslib,以及来自 Node 包的readline 。 安装脚本便于安装、链接和执行。 我已经包含了一个运行,并用内联注释注释了意外的部分。

% ./setup
...
> [email protected] build ~/ts-node/tslib
> tsc --version ; tsc -p src/

message TS6029: Version 1.7.0-dev.20150831
...
> [email protected] build /Users/southerd/devel/tmp/ts-node/tsclient
> tsc --version ; tsc -p src/

message TS6029: Version 1.7.0-dev.20150831
# Expect this to find tslib, but fail type checking.
# See tsclient/app.ts for details
src/app.ts(4,21): error TS2307: Cannot find module 'tslib'.

+ node ./dist/app.js # This works as expected!
What would you ask? What is the meaning of life?
42
+ set +x

我也没有在 tsclient(VSCode 版本 0.7.0 (0.7.0))中获得对 tslib 导入的语言支持,尽管我不完全确定它使用的是哪个 TSC,或者如何更改它。

@DavidSouther TypeScript 编译器检查 package.json 中的 'typings' 字段以查找 '.d.ts' 文件。 通过此更改,我得到src/app.ts(13,7): error TS2322: Type 'string' is not assignable to type 'number'.看起来像一个合法错误

啊,我一定错过了那篇文档。 我仍然遇到第二个问题 - 如何更改 VSCode tsc 版本?

您可以通过"typescript.tsdk"设置向 VSCode 提供自定义 TypeScript SDK 位置 - 它应该指向包含tsserver.js和标准“.d.ts”文件的文件夹。 如果 TypeScript 安装为 npm 包,它将类似于

// Place your settings in this file to overwrite the default settings
{   
    "typescript.tsdk": "C:\\Sources\\bugs\\node\\typescript-example-using-node\\tslib\\node_modules\\typescript\\lib"       
}

有没有人得到一个样品工作? 我尝试了整个下午来玩这个,但我总是以

error TS2307: Cannot find module

目前代码在 node_modules/my-module/ 中。 我定义了一个 index.ts 负责导出所有内容,我在 typescript.main 下的 package.json 中引用了它。 在消费者项目中,我尝试 import {AClass} from 'my-module'; 并导入 my-module = require('my-module'); 它们都导致与 typescript 1.7.0-dev.20150901 相同的错误。

当使用--module commonjs解析具有非相对名称的模块时,编译器将查找与.d.ts中的名称匹配的node_modules\name\index.d.ts或查找package.json对于属性typings ,并加载它指向的 .d.ts 。

从您的描述来看,您似乎将其设置为index.ts ,您并不想编译您的依赖项,您只想使用它们的类型,因此您应该将其设置为index.d.ts

我更新了节点模块解析算法中的步骤以反映实现: https ://github.com/Microsoft/TypeScript/issues/2338

@DavidSouther我显然有完全相同的问题。 当我在 node_modules 中使用依赖项进行编译时,它无法解析模块并输出错误,但是它仍然会生成 js,如果我运行它,它就会运行。 也许是因为我不知道的设置。 我的 d.ts 大部分是空的,我有一个 index.ts 负责导入和重新导出在许多 .ts 文件中定义的模块的所有类。 然后我的 d.ts 文件只引用这个 index.ts 并从 index.ts 中导出所有内容,如下所示:

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

declare module 'my_module' {
    export * from 'index';
}

此外,它仍然以某种方式从 node_modules 编译 ts,因此我应该添加一个干净的任务以在编译后将其删除。 某处是否有总结该过程的功能描述?

编辑:我让它工作,但它超级hacky并且仍然输出错误。 我用 dts-generator 创建了一个 d.ts,然后我像这样导入了我的类:

import MyClass from '../../node_modules/my_module/dist/MyClass';

如果我使用 import MyClass from 'my_module/MyClass' 我让它编译没有错误,但在运行时我得到错误找不到模块'my_module/MyClass'。 使用上述解决方案,它直接指向已编译的 .js 并在运行时以某种方式工作,即使它在编译时输出错误找不到模块。

仍然有从子模块导入的问题(例如 npm install angular2, import { Inject, Binding } from 'angular2/di' 。今晚将努力获得一个包含的测试用例。

不要认为 Angular 已经以 TypeScript 编译器所期望的方式捆绑了它的类型。

这很可能是真的; 我会和他们一起工作,看看结果如何。

此功能在 typescript 1.1 上运行良好,例如https://github.com/Nemo157/typescript_w_node_modules_example

但是在 typescript 1.5.3 中失败了,有什么改变吗?

- - - 更新 - - - - -

我知道它会在 1.6 发布,谢谢

我只能在 1.6.2 中得到这个“半工作”

首先,我在dist目录下用my-lib.d.ts打包一个库,并在package.json文件typings属性中指向这个文件(例如"typings" : "dist/my-lib.d.ts" )

然后我将这个库导入到Test.ts TypeScript 文件中,使用

import { MyObject } from "my-lib"

MyObject已正确导入,并在编译时发出 js 代码。
Visual Studio Code 甚至在MyObject上提供补全

但是我收到编译器警告:
Test.ts(10,60): error TS2306: File '[]/node_modules/my-lib/dist/my-lib.d.ts' is not a module.

Visual Studio Code 实际上会将导入显示为硬错误

从节点包中导入的任何模块都需要是“外部模块”而不是“环境模块声明”。 即没有declare module "foo" {.. }声明,而是文件中的顶级导入或导出声明。

因此,如果您的包“my-lib”是用 typescript 编写的,只需使用--declarations构建它,并将类型设置为您的主模块的.d.ts文件。 如果不是,并且您的类型是您手工编写的,或者是从确定类型中获得的,那么您将需要将其更改为外部模块,或者等到 #4665 解决(希望很快)。

这个限制的原因是这可能会导致你的包用户以后的全局范围污染和冲突。 在#4665 中对此进行了长时间的讨论。

以下是一些用于将来搜索的文档: https ://github.com/Microsoft/TypeScript/wiki/Typings-for-npm-packages

@mhegazy感谢您的快速回复,这确实有助于我自动构建用 Typescript 编写的 commonjs 库,Javascript 和 TypeScript 代码都可以使用这些库。
import/export语法和分辨率最近发展得如此之快,以至于我想现在缺少的是关于如何构建一个的权威指南。

我让它工作了......有一个警告(因此还有另一个问题)

这是设置的简化视图。

该库由 2 个打字稿文件 A.ts 和 B.ts 中的三个对象 A1、A2 和 B 组成; 就像是

来源

A.ts

class A1{}
class A2{}
export { A1, A2 }

B.ts

class B{}
export { B }

我们要公开的内容收集在index.ts中:

export * from './A'
export * from './B'

建造
构建是在 grunt 的帮助下进行的, --module commonjs--declaration标志设置在tsc 1.6.2
构建的最终结果是一棵看起来像

    package.json
    dist/
        js/
             A.js
             B.js
             index.js
        typings/
             A.d.ts
             B.d.ts
             index.d.ts

package.json包含这两个条目:

"main": "dist/js/index.js",
"typings": "dist/typings/index.d.ts"

在 TypeScript 1.6.2 中使用这个库和一个简单的import {A1, A2, B} from "mylib"工作得很好。 多级依赖(即相互导入的库)也可以正常工作。

出现问题的地方是当库依赖于另一个_不是_ TypeScript 库的库时。
假设该库依赖于 Node.js。
在其中一个源文件中将引用 NodeJS 的类型,例如
///<reference path="../typings/node/node.d.ts"/>

在编译后, <reference >指令将在相应的声明文件中结束; 问题是node.d.ts的路径可能是错误的或不存在。
有推荐的方法吗?

_注意_:这个问题可以通过在index.ts文件上使用来暴露库的有趣部分来缓解,因为index.ts没有理由包含<reference >指令并且在 1.6 .2,编译器似乎并不关心 Adts 在引用指令中的路径无效

@bgrieder我们在 Phosphor 中使用tsconfig.json处理这个:
https://github.com/phosphorjs/phosphor-widget/blob/master/src/tsconfig.json

我们只需将所需的任何外部类型添加到编译文件中。 这确实意味着,如果这些外部类型中的任何一个是我们公共接口的一部分,那么代码的使用者也需要提供这些外部类型作为其构建的一部分。 _这没关系_。 如果我们要捆绑这些定义,而消费者使用的其他一些库_也_捆绑了这些相同的定义,则会出现重复的符号冲突。

@sccolbert是的!
我担心删除<reference ...>指令会破坏所有 IDE 上的自动完成功能,但是嘿,不:至少 Visual Studio Code 0.8.0 似乎足够聪明,可以从tsconfig.json中选择这些定义

完成reference 。 优秀的 !

@sccolbert

如果您在其中使用普通的Node.js项目并使用 *.d.ts 怎么办, tsconfig解决方案如何帮助您?

@heycalmdown您只需将d.ts添加到 tsconfig 上的tsconfig files字段,这是一个示例:

https://github.com/phosphorjs/phosphor-widget/blob/master/test/src/tsconfig.json#L11
https://github.com/phosphorjs/phosphor-widget/blob/master/test/src/index.ts#L10

请注意,我们必须在这里使用require而不是import ,这仅仅是因为 expect.js 的d.ts文件是在内部编写的。

好的,我明白了。 您的存储库看起来像是一个很好的例子。

这实际上在 TypeScript 1.6.2 中实现了吗?

如果有,有人可以告诉我我在这里做错了什么吗?:
https://github.com/chanon/typescript_module_example

您可能需要 es6 导入语法。

2015 年 11 月 4 日,星期三,早上 6:10,chanon [email protected]写道:

这实际上在 TypeScript 1.6.2 中实现了吗?

如果是的话,有人可以告诉我我在这里做错了什么吗?:
https://github.com/chanon/typescript_module_example


直接回复此邮件或在 GitHub 上查看
https://github.com/Microsoft/TypeScript/issues/247#issuecomment -153688004
.

我刚刚将其更新为使用 es6 导入语法,但仍然出现相同的错误(找不到模块)。

@chanon node_modules包的package.json需要有一个typings键,它指向一个d.ts文件,_not_ 一个typescript键指向.ts文件,就像你现在拥有的一样。

我们在 PhosphorJS 中专门使用节点模块解析(在 TS 1.6.2 上),它工作正常。 这是一个例子: https ://github.com/phosphorjs/phosphor-widget/blob/f908341cb1d46ada8ad8149e695a75e7ea2fde57/package.json#L6

谢谢@sccolbert ,但是我最初的建议(在本期的顶部)是允许在package.json中使用typescript.main属性,该属性将指向编写的节点模块包的主要 TypeScript 入口点在打字稿中。

这将允许在 TypeScript 代码中导入 TypeScript 模块,而无需d.ts键入文件(甚至不需要生成它们)。

@chanon我只是指出你需要做什么才能让你的代码运行。

@sccolbert我明白了,谢谢。

知道的人可以告诉我是否应该创建另一个请求此特定功能的问题?

与模块解析逻辑(除了这个)相关的主要问题似乎是 #2338 已关闭,而 #5039 似乎是关于支持看起来非常复杂的 SystemJS 样式路径解析。

然而,这个问题最初要求的简单的 CommonJS 样式导入似乎已经被遗忘了? 至少关于 typescript.main 和文件夹作为模块的部分?

如果模块和模块使用者都已经用 TypeScript 编写,我不明白需要(并且希望拥有)d.ts 文件。

它实际上与导入 TypeScript 文件相对而言没有太大区别

而不是必须这样做:
import * as lib from '../relative/path/to/typescriptFile.ts'

或者:
import * as lib from '../../node_modules/myModule/index.ts'

只需让 TSC 能够使用普通的 node_modules 路径解析找到要导入的打字稿文件。 并且至少允许将文件夹作为模块(带有 index.ts)导入,以便在第二个示例中我可以这样做:

import * as lib from 'myModule'

或者是因为“你并不是真的想编译你的依赖,你只是想使用它们的类型”?

@chanon ,您概述的行为是 master 现在的行为。 你能试试typescript@next吗?

#2338 的原始实现添加了一些额外的检查以确保它是 .d.ts,这主要是因为你提到的原因。 您不希望您的包用户在每次编译器调用时编译您的“源”,并根据他们的编译器选项获得不同的输出,您只想共享您的类型。

但是,如果您正在构建这两个包,您可能希望在迭代它们时这样做。 该限制已在 #5278 中删除。

请查看通过 npm 包共享类型的 wiki 页面以获取更多信息: https ://github.com/Microsoft/TypeScript/wiki/Typings-for-npm-packages

感谢您的澄清@mhegazy! 我会尝试一下。

@mhegazy刚刚尝试使用typescript@next ,按预期工作!

这很棒!!

并感谢大家参与此问题和相关问题:+1:

我在上面的问题文本中添加了更新,以解释此功能的实际实现方式。

冒着踏入雷区的风险,这将如何与_不_在 package.json 中具有typings属性的普通 npm 模块一起工作? 尝试将tapeget-parameter-namestypescript@next一起使用会导致它在我的脸上爆炸,因为由于它们没有这些包,它无法找到这些包财产。

当然,它们不是打字稿模块,但我似乎找不到解决方法,并且锁定了 npm 生态系统的很大一部分。

@danpantry通常您会使用专门为该模块编写的普通 .d.ts 文件。 DefinitiveTyped 项目有很多通用模块的 .d.ts 文件。 有一个 tsd 实用程序可以帮助您安装和管理来自 distinctlyTyped 的 .d.ts 文件。

您可以使用普通的模块而无需键入,只需使用普通的 commonjs 要求语法。

例如
var moduleName = require("moduleName")

@chanon在将 ES6 语法与 TypeScript 一起使用时,使用“经典” require语法感觉有点骇人听闻,因此我可以使用 JavaScript 依赖项,但谢谢。 我听说过 tsd 实用程序,但我不确定这将如何影响使用 ES6 导入。除非我必须开始对这些类型的模块使用/// <reference path=...

是的,这是一种方式。 另一种方法是使用 tsconfig.json 文件并将您的根 tsd.d.ts 文件作为第一个文件。

您可能也有兴趣阅读这篇文章,涵盖tsd类型https://angularclass.com/the-state-of-typescript-packages/

@joewood感谢您提供的信息,我上次查看打字稿是在单个.d.ts文件的时代。 优秀的文章

@chanon @danpantry当 foo 的 package.json 没有分型时, import foo = require('foo') (在 node_modules 下带有 foo)有点不幸。

@wmono很好,因为也许 TypeScript 0.80 或更早的import foo = require('foo')语法仅用于导入带有类型的模块。 如果它没有打字,你会使用var foo = require('foo')代替。 所以我会说它就是这样。

@chanon 请原谅我; 用户错误。 看起来 webpack 正在与“裸”的require作斗争,但问题出在其他地方。

对不起,有人可以解释为什么
var foo = require('foo');
适用于标准 node_module 没有打字但
import foo from 'foo';
不是吗? 这只是随意定义的吗? 我不明白为什么后者_不应该_工作-我不在乎外部 JS 库是否有类型。

对不起,有人可以解释为什么
var foo = require('foo');
适用于标准 node_module 没有打字但
从 'foo' 导入 foo;
不是吗? 这只是随意定义的吗? 我不明白为什么后者不应该工作 - 我不在乎外部 JS 库是否有类型。

@harangue因为var require是竞争模块语法之一(这个是commonjs并且列表中的其他包括amd ),它在类型检查系统之外受支持。 如果想使用类型系统,可以使用import require 。 随着 ES6 采用 :hammer: 并说一种语法 (:ring:) 来统治它们……让类型检查系统开箱即用地支持该模块语法是有意义的 :rose:

@basarat感谢您的澄清。 这些图标也很有帮助。 ;) 我仍然不确定为什么 ES6 语法要求类型检查。 是什么阻止编译器说 - “哦,我找不到该模块的任何类型数据,但无论如何我都会导入它”。 这种(相当直观的,IMO)行为会产生什么副作用?

这对于那些从 ES6 到 TypeScript 的人(比如我自己)来说是令人困惑的。 我希望 ES6 模块导入语法在 TypeScript 中的行为方式相同,但是对于大多数 npm 包来说它根本不起作用......

但是对于大多数 npm 包来说,它根本不起作用......

@teohhanhui我很困惑。 您能否举一个具体示例说明您要实现的目标以及您尝试实现的代码。 这只是为了让我可以帮助你:rose:

import koa from 'koa';

targetes6 $ 时给出Cannot find module 'koa'. (2307) #$

koa (当然)在node_modules

但是,koa 不会在他们的项目中发布 Typescript 类型。 这仅适用于在其项目中发布 .d.ts 并将其列在其 package.json 中的包。 因为 koa 没有,所以您需要另外安装来自“绝对类型”的那些类型定义。

这似乎是一个糟糕的决定,因为人们期望import语句就像在普通 ES6 模块中一样工作。 (是的,我知道,大多数npm包都是 CommonJS 模块,但在这里支持它们仍然是明智的做法。)

我是 TypeScript 的新手,所以我可能有错误的理解,但类型定义不是可选的吗? 为什么我不能在没有类型定义的情况下导入包? 我不想被迫寻找/创建类型定义。

切换到旧版 TS 模块导入语法不是一种选择,因为在使用es6目标时不允许这样做。

我是 TypeScript 的新手,所以我可能有错误的理解,但类型定义不是可选的吗?

这是不正确的。 类型定义_在你自己的代码中_可以是隐式的,这意味着 Typescript 编译器会“弄清楚”。 你永远不会通过 Typescript 编译器传递第三方库,所以编译器永远不知道这些类型是什么。 如果你打算使用第三方代码,你真的应该学习如何使用 Definitive Typedtsd工具。

@basarat 的这个答案怎么样?

http://stackoverflow.com/a/27434010/1529493

为什么不能像这样导入没有类型定义的非 TypeScript 模块?

他们可以: const koa = require(‘koa’)

2016 年 1 月 21 日 14:47,Teoh Han Hui [email protected]写道:

为什么不能像这样导入没有类型定义的模块?


直接回复此邮件或在 GitHub 上查看https://github.com/Microsoft/TypeScript/issues/247#issuecomment -173574234。

@bgrieder我指的是 ES6 导入语法。

(就是你用的是commonjs),如果没有,就用巴萨拉特的declare var答案

2016 年 1 月 21 日,14:48,布鲁诺·格里德·布鲁诺。 [email protected]写道:

他们可以: const koa = require(‘koa’)

2016 年 1 月 21 日 14:47,Teoh Han Hui < [email protected] [email protected] > 写道:

为什么不能像这样导入没有类型定义的模块?


直接回复此邮件或在 GitHub 上查看https://github.com/Microsoft/TypeScript/issues/247#issuecomment -173574234。

我认为要提出的观点是 TypeScript 的目标之一是有效的 JS 应该是有效的 TS。 对于 ES6 模块语法,TS 应该在有或没有类型声明的情况下编译它。 如果没有类型声明, import语句应该只解析为any

我完全同意。

@乔伍德👍

2016 年 1 月 21 日,14:52,Joe Wood [email protected]写道:

我认为要提出的观点是 TypeScript 的目标之一是有效的 JS 应该是有效的 TS。 对于 ES6 模块语法,TS 应该在有或没有类型声明的情况下编译它。 如果没有类型声明,import 语句应该只解析为 any。

我完全同意。


直接回复此邮件或在 GitHub 上查看https://github.com/Microsoft/TypeScript/issues/247#issuecomment -173575301。

FWIW 这种行为(期望提前输入所有 3rd 方库)是导致我使用 Flow over TypeScript 的原因。 虽然 TypeScript 具有 IDE 支持,但期望提前输入所有内容并不合理,尤其是当有大量库时:

  • 不在DefiniteTyped上
  • 没有./typings文件夹
  • 我没有时间自己打字

我宁愿不必与那些有助于我发展的东西作斗争。

@danpantry ,为什么不将您的 lib 入口点声明为 any,编译器将对此感到满意:

declare var $: any;

这是一种解决方法。 但我认为 TypeScript 尊重 ES6 模块代码并优雅地回退是一个好主意。 如果顶层模块没有类型化,我看不出为什么 import 语句不能回退到any而不是报告错误的充分理由。

我更希望编译器尽早且大声地失败(当前行为),而不是让运行时问题在不被注意的情况下蔓延。 我更喜欢明确地有一个declare var foo: any行来表示“这是不安全的,我知道我在做什么”。

当没有可用的类型时,为什么不只允许 ES6 导入语法一个简单的编译器警告导入已解析为any ,而不是“防止发出”错误?

2016 年 1 月 21 日 18:42,S. Chris Colbert [email protected]写道:

我更希望编译器尽早且大声地失败(当前行为),而不是让运行时问题在不知不觉中蔓延。 我更喜欢明确地声明 var foo: any line 来表明“这是不安全的,我知道我在做什么”。


直接回复此邮件或在 GitHub 上查看https://github.com/Microsoft/TypeScript/issues/247#issuecomment -173649845。

无论如何,编译器目前声称它找不到模块,而实际上它只是拒绝导入没有类型定义的模块。 这不是很有帮助,是吗?

^^^ 这千遍。 当我第一次遇到这个问题时,没有迹象表明我做错了什么。 谷歌搜索了很长时间才最终弄清楚发生了什么(顺便说一下,我认为这个问题是唯一可以找到的记录它的地方之一)。 这太没必要了。 随着 Angular 2 将 TypeScript 带入主流,我只能想象有多少 Stack Overflow 问题会来自这种行为。

非常同意糟糕的错误处理描述,并将其作为任何导入!

@sccolbert我同意应该有一个通知,但我不确定它应该是一个失败 - 也许是一个警告

我同意@mhegazy和@sccolbert。

如果出现问题,我更希望我们的生产构建脚本(即 CI 服务器)大声抱怨。

嗨,大家好,
这种行为简直让我发疯。 定义文件不是最新的或未注册到package.json
最后TypeScript产生JavaScript ,所以,在我们都使用这种语言之前,请对世界其他地方保持温和,因为它为母语提供任何其他翻译语言的库,比如你的.
我真的很想知道TypeScript是否想留在JavaScript生态系统中(我会说“整合”,因为它看起来还不存在)还是想要分开生活,因为第二个选项会让我去做别的事情。
为了实现.tsconfig文件中的切换,我们可以说我们是否需要严格导入。
至于 CI 大声抱怨,在JavaScript中有相应的测试框架。 无论如何,你不会在运行时进行类型检查,糟糕的导入检查是不太重要的问题。
一切顺利。

@devel-pa,这些错误是为了告诉你编译器不知道你的导入是什么。 任何后续使用导入都无法检查。

关闭错误并不能解决任何问题。 它只是默默地将这些“未知数”推送到整个系统中。 没有类型信息,编译器无法警告您什么是安全的,什么不是。 这就是 TypeScript 的重点:)

至于生成的JS。 没有任何 TS 类型错误会阻止您生成输出。 如果您不关心类型错误,请忽略它们。 编译器仍会生成匹配的 .js 文件。

缺少定义的解决方案是声明它们。 您不必声明模块的完整形状,只需声明名称即可。 这允许编译器知道有一个模块“myLibrary”,它可以警告您模块名称中的拼写错误,更重要的是,不会取消选中其他模块。

declare module "myLibrary" {
    var a: any;
    export = a;
}

如 #6615 中所述,TypeScript 应该很快支持这种更短的形式。

我认为问题是这导致入职项目的痛苦。 我认为这个问题类似于隐式 any 。 现在这些导入基本上是隐含的 void 。 当然,这些错误可以忽略不计,但这会引起很多噪音,并且违背渐进式输入和有效 JS 是有效 TS 的理念。

对于刚接触 TS 的人,我可以理解这里的困惑。 如果他们在 JS 中积极使用 ES6 模块语法,为什么它不能在 TS 中使用? import fromvar x = require相比有何特别之处 - _is_ 被视为隐含的 any。 最初import是一个特定于 TS 的关键字,因此它暗示开发人员希望将该模块视为一个类型化模块。 既然它不是 TS 独有的,我认为不能做出这样的假设。

隐式任何错误都在没有类型的变量声明上。 但是今天使用未声明的变量仍然是一个错误。 对于 jquery,你仍然需要declare var $;某个地方让编译器知道你真的想要一个名为$的全局变量,而不仅仅是错误输入它。 模块或多或少是相同的,编译器需要知道有一个名为"myLibrary"的模块,它不是一个错误类型的名称。 所以只需为它添加一个声明。

无论如何,#6615 应该支持添加declare module "*";以匹配系统中的所有模块,尽管我认为如果你这样做,你不会从你的工具中得到任何帮助,而且你正在为自己做一个痛苦的准备稍后当您决定删除它时进行转换。

@mhegazy我理解你的推理,但我真的对这个理由有疑问

基本上,您告诉我们设计一个“占位符”模块定义,问题就会消失。
这种风险是,一段时间后,在一个合理规模的项目中,这些“占位符”定义可能会逐渐增加并被埋在一些类型目录中。 没有更多的警告,每个人都会忘记它们,当出现问题时,查看每个模块定义以查看哪个是占位符,这将是一件痛苦的事情。

对于类型化的模块(package.json 中带有typings条目的模块)情况更糟,我们假设正确类型化的模块实际上可能在内部使用这些占位符。

我知道这些占位符是无法阻止的,但我更希望至少不鼓励它们。
应该提倡的是,默认情况下,导入解析为any ,并且编译器在每次编译时都会发出警告。 所以至少,查看编译器/CI 日志,我们知道有些东西可能是可疑的,需要改进。

(另一方面,如 typescriptlang.org 主页所述,Typescript 是 Javascript 的“超集”,恕我直言,任何有效的 ES6 语法都应该按原样吞下)

对我来说,变通办法和把垃圾藏在地毯下是最糟糕的(除了 ES6 super)。
我一直在使用 JS 库,通常使用最新版本作为我工作的一部分,并在家里进行额外的编码。 90% 没有输入或有旧的定义,9% 的输入不是很好(因为编译器不知道为所有文件创建一个定义)。 我的都没有很好的defs,出于同样的原因以及我的目标是JS,我认为我不必关心原始语言。
另外,我已经看到了存在“未知”库的原因。 不,一点也不,如果开发人员不知道和理解使用什么库,为什么要打扰他们,我知道我为什么要使用东西以及为什么要添加到堆栈中。 警告和测试(如果存在)就足够了。
请首先使用 JavaScript,这是目标和生态系统。 TS 只是用于在编译时更好地编码和类型检查的附件,但仅适用于我们正在构建的内容。 其余的都不是打字稿业务。
我想提一下,我也反对 nmps peerDependencies ,我是选择的人,而不是软件。 命令式编程,宝贝。

从 JSPM 目录而不是 node_modules 导入模块怎么样?

编辑:我找到了一张现有票-> https://github.com/Microsoft/TypeScript/issues/6012

在使用 SystemJS 加载目录时,我刚刚遇到了 Node 模块解析的问题:

显然,虽然 Node(以及 TypeScript)理解路径实际上是一个目录并因此加载index.ts ,但 SystemJS 并没有这样做,这意味着有效的 TS 代码实际上将无法在浏览器中加载。

对于具有 index.ts/js 入口点甚至使用 package.json main属性的节点模块也是如此。 这稍微容易使用,因为向 SystemJS 添加包配置很容易(尽管重复)。 在使用任意目录时,这并不容易。

什么是合适的解决方案?

当前不支持 JSPM 自动模块解析。 这由https://github.com/Microsoft/TypeScript/issues/6012 跟踪。

建议使用路径映射模块解析支持,参见https://github.com/Microsoft/TypeScript-Handbook/blob/release-2.0/pages/Module%20Resolution.md#path -mapping

以上说明只是一个开始,但有些人可能需要做一些额外的事情......您可能需要添加
<Folder Include="node_modules" />
到 .njsproj

更多信息

我正在使用 gulp 构建,并且在 .nsproj 中有 TypeScriptCompileBlocked。 要解决在 VS 15 Preview 5 中调试断点的问题(我收到“框架不在模块中”错误)以及我在使用智能感知时遇到的问题,我必须添加到 .njsproj 。 当我导入项目时,该目录已经存在,但设置为 git-ignore。 这就是我为什么 Visual Studio 忽略它的理论(也许它应该对 node_modules 有一个自动例外?)

除了调试和智能感知工作之外,我也不再看到智能感知错误,超出了我在 gulp 构建时看到的完全相同的错误,这是预期的。

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