将JavaScript模块或库导入TypeScript



多年来,我一直在与这个问题作斗争。我似乎不知道如何可靠地使用TypeScript中的JavaScript库。

我似乎是意外地让它工作起来的,然后继续前进,多年来都没有重新访问这样的代码,直到外在的变化迫使它崩溃,就像今天我更新VS 2019时一样。

我花了好几天时间阅读有关模块、需求和加载程序的内容,但我越来越困惑。

示例。我想在我正在编写的TypeScript.ts文件中使用DayJS。

这是示例代码。

import * as dayjs from 'dayjs'
dayjs().format()

我不能理解的是,由于两个原因,这种语法从未在任何库中适用于我:

  1. 它省略了指向dayjs文件夹的路径,对我来说应该是lib/dayjs
  2. 它没有指定文件,我以前对moment.js的工作引用指向moment.js文件

不清楚'dayjs'是文件夹还是文件。

复合学习的两个问题是:

  1. TypeScript无论找到了什么但找不到要加载的东西,还是路径无效/不存在,都会给出相同的错误

错误TS2307:找不到模块"lib/dayjs"或其相应的类型声明。

  1. TypeScript似乎允许导入指向没有文件扩展名的文件,例如,此代码似乎满足并消除了上述错误,因为磁盘上有一个index.js文件。这个";灵活性";导致歧义和混乱。

    import * as dayjs from "lib/dayjs/index";

TypeScript文档本身没有给出从JavaScript库导入的示例,只是从其他.ts文件导入,而没有扩展名。

以下是磁盘上的DayJS文件。

libdayjsesm          
libdayjslocale       
libdayjsplugin       
libdayjsCHANGELOG.md 
libdayjsdayjs.min.js 
libdayjsindex.d.ts   
libdayjslocale.json  
libdayjspackage.json 
libdayjsREADME.md    

以下语法没有错误,TypeScript对其进行编译,甚至加载intellisense/文档。

import * as dayjs from "lib/dayjs/index.d.js";

但是那个文件不存在!所以我的网络应用程序无法运行,因为Chrome无法获取它。但是下面的无法编译,即使文件存在!!

import * as dayjs from "lib/dayjs/index.d.ts";

错误TS2691:导入路径不能以扩展名'.d.ts'结尾。请考虑导入"lib/dayjs/index"。

然而,当我按照错误的建议操作时,这也是Chrome的404

有人知道地球上发生了什么吗??

在TypeScript中使用JavaScript库的最终方法是什么?


更新

我现在已经使用了tsconfig.json中的baseUrlpaths设置来获得在TypeScript中编译的导入语句,该语句在部署/托管时可转换到200 OK的路径。

"compilerOptions": {

"target": "es5",
"module": "es2015",
"baseUrl": "./wwwroot/",
"paths": {
"lib/dayjs/dayjs.min.js": [ "lib/dayjs/index" ]
},
...

TypeScript intellisense告诉我,我对DayJS的使用是正确的。

var f = dayjs().format("dddd D MMMM YYYY");
var t = dayjs(date).format("dddd D MMMM YYYY");

但在浏览器中,Chrome报告了一个错误:

TimeHelpers.js:16 Uncaught TypeError:dayjs不是函数

我不知道。我可能会再试一次,但这给了我一些其他奇怪的错误,global在其源代码的顶部是未定义的。

否则,显然我可以将dayjs声明为Any以满足TS编译器的要求,然后使用老式的<script>标记将其加载到页面上。这太疯狂了。


更新

我已经取得了足够的进展,在tsconfig.json中添加了以下两行,并更改为

"esModuleInterop": true,
"allowSyntheticDefaultImports": true,

import dayjs from "lib/dayjs/dayjs.min.js"

我现在得到了一个其他人得到的错误,这让我松了一口气,因为至少我现在知道不要使用DayJS。

https://github.com/iamkun/dayjs/issues/313


更新

今天是漫长的一天,但取得了一些进展。

  • 我根据我从文档中重读和重新解释的内容,在tsconfig.json中重新配置了baseUrlpaths
  • 我已经改为尝试luxon,但有问题
  • VS代码和编译器看到了Luxon打字员和intellisense的工作
  • 我在gulpfile.js中添加了一个小调整,改变了导入路径,这样Chrome就可以在部署时加载它
  • 明天我会回去尝试一下,看看我是否能运用我的新知识,让其中一个包发挥作用

我会在一切就绪后发布一个完整的解释。

我有很多相同的挫折!很难让TypeScript与JavaScript很好地配合使用,而Microsoft文档又太迟钝了!

在您的情况下:指向库的路径总是在node_modules中查找,因此在这种情况下,您不需要添加完整路径。

您也永远不需要import一个.d.ts文件。您只需将.d.ts文件放在工作文件夹中的某个位置,VS Code就会检测到它

如果您有moment.js.d.ts文件,您将在VS代码中获得类型完成。当您使用<script>文件加载它时,不需要import moment.js

关于导入扩展:这取决于您是使用本机ES6模块还是使用模块绑定器。在后一种情况下,您不需要添加文件扩展名,因为模块bundler会为您解决这个问题。

以下是我学到的东西。买家要小心:这可能是不正确的。

对模块的本机支持对于JavaScript来说相对较新。多年来,为了分解大型代码库,库提供了对模块化代码的支持。

JavaScript通常作为一种应用程序语言在浏览器外使用,比如Node.js;浏览器中的客户端代码、服务器端和桌面独立应用程序代码。

存在各种各样的约定和语法来解决这个问题,如CommonJS(又名ServerJS)、AMD、UMD和ESM/ES6模块。

我不完全确定这一点,但我认为对于浏览器内的使用,您需要做额外的工作来使用模块。

  • 添加一个额外的编译/编译/绑定工具,将所有模块代码文件拉到一个可部署的代码文件中。

  • 添加一个模块加载器库,可以下载依赖的模块JS文件。

  • 对于ESM,请确保用户的浏览器是最新的,然后确保导入语句指向JS文件的有效URL,包括其扩展名。

Node.js运行时可能有自己的语法和模块加载程序,我不知道。

TypeScript只是一个JavaScript编译器/传输程序,它是为Node.js和浏览器应用程序设计的。它的输出只是故事的一部分,通常需要进一步的操作或处理。与传统的代码编译器不同,仅仅因为编译器是愉快的并且没有错误,并不意味着TS生成的JS会自动运行。

例如,在我的案例中,我需要在Gulpfile.js中执行查找-替换步骤,将一些import thing from "name"模块名称更改为工作URL。

TypeScript需要一个打字员文件来帮助它的编译器。此文件仅在编辑期间有用,用于智能感知和检查您对库的使用是否完美。一旦运输,就不会使用。现在,许多包都附带了打字员文件、.d.ts文件,TS通常只是神奇地找到它们。

NPM包通常不是为ES6构建的。包中的JS文件本身可能是经过转换的源代码的产物。文档可能会显示使用ESM的语法,但可能不会说明要使用包中的哪个JS文件,甚至可能包括一个或多个兼容文件。

您甚至可能需要在一个包中进一步编译、传输或捆绑JS文件。这通常是环境知识,没有明确说明。

在Luxon的情况下,该包不包含ESM兼容代码,但包含CommonJS和<script>标签的代码。ESM兼容代码是手动下载的。

moment.js的情况下,如果文档说明要导入哪个moment.js文件,那么问题早就解决了。

我自己找到的最接近的是moment/src/moment.js,它看起来像ES6代码,有一个默认导出,但在浏览器中失败了,因为同一个文件从JS文件导入,而没有指定.js扩展名,这是web浏览器不支持的。

最后,在SO上的某个人的帮助下,我在dist文件夹中发现了一个看起来像是一个大的、单个的、捆绑的moment.js,带有默认导出。我有没有提到JS包的作者使用了晦涩难懂的名字?

不幸的是,我一直在尝试许多不同的约会时间包,并对TypeScript设置进行了大量的修改,所以我没有早点发现这个问题——我可能直到最后8个小时才知道该找什么。

  • 在您的NPM包中,找到大的";预捆扎";(?)带有ES6外观代码的JS文件,通常可以通过在文件中搜索export default来识别,通常在底部。

  • 使用以下tsconfig.json

    "目标":"es6";,"模块":"es2015";,"esModuleInterop":是的,"allowSyntheticDefaultImports":是的,//根据需要设置baseUrl和路径

  • 使用以下语法消费,例如moment.js

    从"moment/minum"导入moment//根据需要更改模块名称/路径

  • TSC运行后,使用查找-替换任务将TS发出的模块名称转换为已部署的URL。

  • 在打开控制台的情况下,在浏览器中进行部署和硬重新加载。

最新更新