TS引用JS模块

为TypeScript引用的JS写声明文件

写TypeScript声明文件的时候会有三个困惑,一个是声明文件是什么?一个是声明文件怎么写?还有一个是TS依据什么规则找到我们的声明文件或者说模块。

第一个问题:按照我的理解声明文件就是告诉TS编译器有哪些模块?有哪些变量?变量分别是什么类型?所以如果说原本就是TS写的代码这些都是具有的,但是JS写的代码就不会有这些,因为这些强类型是TS对JS的扩展,JS没有这个特性。

第二个问题:这个需要看官方的文档(下文也简单举例一些例子),并且要仔细看,如果语法错误也会带来很多困扰。

第三个问题:这个涉及到TS的模块解析,详情请看 官文–模块解析 ,这篇文档写的很详细,足以解释。这里列举两个我认为的重点:相对模块导入不能解析为一个外部模块声明。自己声明的模块(declare module “name”{} 这个name不能是相对的也就是说不能以 ./``````../``````/开头),还有一个是配置文件tsconfig.json文件中{"compilerOptions": {"traceResolution": true}}可以追踪模块解析的过程,想要深入了解这是一个好的方法,但是据我观察只能在vscode控制台中才能打印出来。

注:TS引用JS库如果JS库没有对应的声明文件编译器是不会报错的,因为没有声明文件的JS模块会隐式的获得any类型,除非tsconfig.json中有noImplicitAny: true这样的配置。

已经过验证上面的解释是没错的还原如下:

注:TS引用JS库如果JS库没有对应的声明文件编译器是不会报错的,因为没有声明文件的JS模块会隐式的获得any类型,除非tsconfig.json中有noImplicitAny: true这样的配置,指明了如有隐式转换为any类型就报错,才会报错。

TypeScript是JavaScript的超集

TypeScript会进类型检查,什么鬼?JS没有这个东西

使用TS进行开发也可以使用当前丰富的JS库,很多JS库有写好的TS声明文件,但是如果是我们自己写的JS库想要在TS中使用就需要我们自己去编写声明文件(.d.ts文件),怎么写?这就是极具个人经验主义的本文要解释的问题,如有谬误感谢指正。

本文主要是对此刻所得的整理。

下面示例基于webpack配合ts-loader,开发环境的配置可以参考我的另一篇文章基于webpack3.x从0开始搭建React开发环境。

JS库文件和对应的TS声明文件

现有的JS库可能有不同的写法,有的库导出属性,方法等,有的库导出一个类还有的库只导出一个函数。下面针对不同类型的JS库来写不同的声明文件。

声明文件也分为两种,一种是全局类型声明另一种是模块导出声明而这两种只是声明文件的写法和JS库的写法没有关系,并不是说全局的库就需要使用全局类型声明的写法,模块的库就用模块导出的写法。

// 文件目录结构如下
-- project|-- node_modules|-- simple|-- index.js|-- lib1.js|-- lib2.js|-- src|-- types.d.ts|-- app.ts
// /node_modules/simple/index.js
// ES原生模块写法,并且导出了属性和方法
let a = 1;function geta () {return a;
}function seta (val) {a = val
}export {geta, seta, a, a as default}
// 为simple/index.js写全局类型声明,在types.d.ts中添加如下代码
declare module "simple" {let a: number;export function geta(): void;export function seta(n: number): void;export default a;
}
// app.ts  使用三斜线指令引入声明文件
/// <reference path="./type.d.ts" />
import a, {geta, seta} from 'simple'console.log(a)
// /node_modules/simple/lib1.js
// 导出一个类
function Ab () {this.a = 1
}Ab.prototype.seta = function (num) {this.a = num
}Ab.prototype.geta = function (num) {return this.a
}exports.Ab = Ab
// 在type.d.ts文件中添加declare module "simple/lib1" {export class Ab {private a;seta(n: number): void;geta(): number}
}
// app.ts  使用三斜线指令引入声明文件
/// <reference path="./type.d.ts" />
import a, {geta, seta} from 'simple'
import {Ab} from 'simple/lib1'// 得以验证
console.log(new Ab())console.log(a)
// /node_modules/simple/lib2.js
// 只导出一个函数
module.exports = function getRandom () {return Math.random()
}
// 在type.d.ts文件中添加
declare module "simple/lib2" {let getRandom: () => number;export = getRandom;
}
// app.ts  使用三斜线指令引入声明文件
/// <reference path="./type.d.ts" />
import a, {geta, seta} from 'simple'import {Ab} from 'simple/lib1'import getRandom = require('simple/lib2')// 得以验证
console.log(getRandom())console.log(new Ab())console.log(a)

上面给出的只是全局声明的写法,下面会针对上面的js库重新换成模块导出声明的写法

目录改成如下形式,app.ts文件无需做大的改动只需要将三斜线指令去除即可,一般情况下即使去除该指令types.d.ts文件还在的话TypeScript编译器还是会将该文件加载编译,这与配置有关。

并且根据我的观察发现,修改声明文件并不会马上起作用,比如在声明文件中加了一个方法,在使用的时候TypeScript编译器还是会报错说这个类型没有这个方法,需要重启webpack-dev-server(我用的是这个)

// 文件目录结构如下
-- project|-- node_modules|-- simple|-- index.js|-- index.d.ts|-- lib1.js|-- lib1.d.ts|-- lib2.js|-- lib2.d.ts|-- src|-- app.ts
// index.d.ts
let a: number;
export function geta(): void;
export function seta(n: number): void;
export default a;
// app.ts
import a, {geta, seta} from 'simple'// 得以验证
console.log(geta())
//lib1.d.ts
function Ab () {this.a = 1
}Ab.prototype.seta = function (num) {this.a = num
}Ab.prototype.geta = function () {return this.a
}
exports.Ab = Ab
import a, {geta, seta} from 'simple'
import {Ab} from 'simple/lib1'// 得以验证
console.log(new Ab().geta())console.log(geta())
// lib2.d.ts
let getRandom: () => number;
export = getRandom;
import a, {geta, seta} from 'simple'
import {Ab} from 'simple/lib1'
import getRandom = require('simple/lib2')// 得以验证
console.log(getRandom())console.log(new Ab().geta())console.log(geta())

不想为JS类库写具体的声明文件怎么办?

在全局类型声明的文件中声明一个模块,模块什么都不做即可(这里还可以更加彻底,如文章开头的更新):

// types.d.ts  替换simple声明如下
decalre module "simple"

node_modules下的@types文件夹

默认所有可见的"@types"包会在编译过程中被包含进来。什么叫默认可见?就是说node_modules/@types文件夹及他的子文件夹下所有的包都是可见的,还包括 ../node_modules/@types../../node_modules/@types等。

有什么用呢?可以将自己的全局声明文件放在这个文件夹里面,这样就可以自动加载。

上面一段话也是错的,并不会自动的导入,所上面摘抄官网的一段话到底啥意思就不得而知了。可能意思是会被包含但是就看能不能正确解析了。

注:node_modules/@types 是TS声明文件默认位置,并且只能是全局声明的写法

上面一句话就错了,@types里面的声明可以是模块的写法,也可以是全局写法。只要是一个模块(顶层的import/export这个文件就是一个模块或者declare “name” module { export … })就行了。

全部重写node_modules下的@types文件夹如下

在@types文件夹下的声明的包和在node_modules下的包其实一样,在node_modules没有解析到合适的.ts/.tsx/.d.ts 文件之后,TS编译器便会来到node_modules/@types文件加下寻找,如下:

import {a} from 'abc'

这种情况@/types/abc.d.ts有两种写法

export const a: number; // 只要导出即可
declare module "abc"{export const a: number;
}

同样这个@/types/abc.d.ts的目录也可以这样:

@types/abc/index.d.ts

关于@types的配置

这个配置有两个:一个是typeRoots表明声明文件的根文件夹,还有一个是types表明需要包含的声明文件包(文件夹名,我试过types/efg.d.ts 这样的并不能用,需要这样types/efg/index.d.ts)。

上面划掉的部分是错的,可以用types/efg.d.ts这样的。

注验证的时候最好配合types因为上文提到过,TS编译器会默认包含所有的ts文件,所以如果不过滤,设置的typeRoots没有意义因为默认就是全部包含的。

极具个人经验部分

观察上面模块导出声明和全局类型声明两种写法发现写法差别并不大,主要区别就是声明文件放置位置不同,全局会多一个declare module "name1"。再仔细观察会发现这个name1和import … from "name2"中的name2是一样的,然后对于全局的声明文件还在需要的时候使用 /// <reference path = "path" />引用进来。所以我怀疑(因为我还没有了解到是不是事实)import … from "name"这个其实引用的是我们在声明文件中定义的module。

什么是module?

如果一个JS文件在顶层具有import或者export那么这个文件就是一个模块(模块名对应的就是文件名),在模块中定义的变量并不会暴露在全局环境下。

而上面模块导出的写法 declare module "name"{}就相当于声明了一个模块(一个文件?)。

全局类型声明的思考

全局类型声明中只是声明了相关模块当然也可以声明其他东西,而是用全局类型声明的方法,不是import(这不是一个模块)而是三斜线指令使用 /// <reference path="path" />这样 .d.ts 文件中的声明就被编译器读取了,之后可以再下面import … from "module"只是这个module是我们声明出来的,并不会在对应的路径下找到相关的 .d.ts.ts.tsx文件。

如果是模块导出写法必须和库在一起,否则并不知道属于哪个模块的声明,但是@types怎么解释

模块导出声明的思考

模块导出声明的写法是在 .d.ts 文件顶层是有export的所以一个文件是一个模块,如果单独引入(要使用import来引入)模块的话并不知道这个模块是哪个库的声明文件,所以需要和JS库放在一起,并且名字还要一样(后缀名不一样)。但是@types怎么解释???

关于@types的思考

见上文node_modules下的@types文件夹

参考

TypeScript 的声明文件的使用与编写

TypeScript 的两种声明文件写法的区别和根本意义

TypeScript文档

TS模块解析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/362197.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

angular模块

深入浅析AngularJS中的模块 模块是AngularJS应用程序的一个组成部分&#xff0c;模块可以是一个Controller、Service服务、Filter过滤器、directive指令&#xff0c;这些都属于模块。 大多数的应用程序都有一个函数入口main文件&#xff0c;用它来进行初始化&#xff0c;加载…

对象应该是不可变的

在面向对象的编程中&#xff0c;如果对象的状态在创建后无法修改&#xff0c;则该对象是不可变的 。 在Java中&#xff0c;不可变对象的一个​​很好的例子是String 。 创建完成后&#xff0c;我们将无法修改其状态。 我们可以要求它创建新的字符串&#xff0c;但是它自己的状…

开源GIS---.Net系列

NASA World WindNASA World Wind是使用.NET开发的一个Windows窗体系统&#xff0c;以地球外观看得角度提供全球定位功能&#xff0c;类似于Google Earth。它结合了美国国家航空航天局(NASA)从卫星拍摄的图像&#xff0c;这些图像应用于Blue Marble, Landsat 7, SRTM, MODIS 以及…

TypeScript中怎么用接口(interface)描述类(静态部分与实例部分)

TypeScript是JavaScript的超集 在看TypeScript官方文档的接口一节的时候对于类的静态部分与实例部分产生了疑问&#xff0c;通过努力得到自认为相对合理的解释&#xff0c;写下此文以记所得&#xff0c;如有谬误感谢指正。 文中大部分代码示例来自TypeScript官网 TS中的接口…

Java黑科技之源:JVMTI完全解读

Java生态中有一些非常规的技术&#xff0c;它们能达到一些特别的效果。这些技术的实现原理不去深究的话一般并不是广为人知。这种技术通常被称为黑科技。而这些黑科技中的绝大部分底层都是通过JVMTI实现的。 形象地说&#xff0c;JVMTI是Java虚拟机提供的一整套后门。通过这套后…

npm install --save

1. npm install&#xff1a;本地安装 2. npm install -g&#xff1a;全局安装 我们在使用 npm install 安装模块或插件时&#xff0c;有两种命令把它们写入到 package.json 文件中去&#xff0c;在 package.json 里面体现出的区别就是&#xff0c;使用 --save 安装的插件&…

其他一些单元测试技巧

在我以前的文章中&#xff0c;我展示了有关JavaBeans单元测试的一些技巧。 在此博客文章中&#xff0c;我将提供有关单元测试某些相当常见的Java代码的另外两个技巧&#xff0c;即实用程序类和Log4J日志记录语句 。 测试实用程序类 如果您的实用程序类遵循与我倾向于编写的相同…

常见的CSS布局

各种常见的CSS布局 在工作中会经常用到很多的布局方式&#xff0c;这里总结一下所遇到的布局&#xff0c;会持续更新。 悬挂布局 实现这种布局的方式有很多&#xff0c;这边主要挑两个&#xff0c;如下&#xff1a; 方式一&#xff1a;使用浮动和块级格式化上下文特性 这种…

netflix数据处理2(转)

原始数据&#xff1a;$head -10 mv_0006890.txt6890:1735266,1,2004-04-021008399,1,2004-06-222360117,2,2003-11-081294425,2,2004-03-15439931,4,2004-03-271583311,1,2004-03-112431832,3,2005-02-13620771,2,2004-03-201110906,1,2004-03-04结果数据&#xff1a;user_id m…

jQuery 效果

显示和隐藏 1. show(speed) &#xff1a;speed 可以取&#xff1a;slow/fast/毫秒 1 $("#show").click(function(){2 $("p").show(1000);3 }); 2. hide(speed) &#xff1a; 1 $("#hide").click(function(…

OSCP-Kioptrix2014-2 漏洞利用

pChart 2.1.3 文件包含漏洞 搜索漏洞查看漏洞理由代码:hxxp://localhost/examples/index.php?ActionView&Script%2f..%2f..%2fetc/passwd 之前的8080端口禁止访问,看看apache的配置:http://192.168.1.78/pChart2.1.3/examples/index.php?ActionView&Script%2f..%2f..…

使用Drools跟踪输出

Drools 6包含一个跟踪输出&#xff0c;可以帮助您了解系统中正在发生的事情&#xff0c;事物执行的频率以及多少数据。 这也有助于理解Drools 6现在是基于目标的算法&#xff0c;它使用链接机制链接评估规则。 有关此的更多详细信息&#xff1a; http://www.javacodegeeks.co…

CodeSmith注册机,支持5.2.2和5.2.1版

CodeSmith&#xff0c;不用说了&#xff0c;大名鼎鼎的代码生成工具。最早是免费的&#xff0c;后来收费啦这个注册机是针对目前新的CodeSmith 5.2.2的&#xff0c;支持Professinal和其他版本。使用的方法&#xff1a;安装原版的试用版本&#xff0c;从官方网站下载运行试用版&…

JS中与正则相关的方法

前面有一篇文章大体介绍了一下JS中正则表达式&#xff0c;而使用正则表达式还需要配合JS中的相关方法&#xff0c;分别是String对象和RegExp对象的方法。今天就来具体介绍一下这些方法。 使用这则表达式的方法可以分为两类&#xff0c;一个是String的几个方法&#xff0c;还有…

JS基础:求一组数中的最大最小值,以及所在位置

1 var arr [0, 5, -3, 6, 2, -6, 10];2 //定义一个最大值和一个最小值&#xff0c;把他们的索引值赋值给固定的两个变量3 var maxValue arr[0];4 var minValue arr[0];5 var maxIndex 0;6 var minIndex 0;7 for …

linux epoll,poll,select

epoll函数用法&#xff0c;还有点poll和select 1&#xff0c;LT的epoll是select和poll函数的改进版。 特点是&#xff0c;读完缓冲区后&#xff0c;如果缓冲区还有内容的话&#xff0c;epoll_wait函数还会返回&#xff0c;直到把缓冲区全部读完。 2&#xff0c;ET的epoll&#…

λ和副作用

总览 Java 8添加了诸如lambda和类型推断之类的功能。 这使语言不那么冗长和简洁&#xff0c;但是它带来了更多的副作用&#xff0c;因为您不必对所做的事情那么明确。 Lambda的返回类型很重要 Java 8推断闭包的类型。 一种方法是查看返回类型&#xff08;或是否返回任何内容&a…

sessionStorage和localStorage的用法,不同点和相同点

一&#xff0c;共同点 &#xff08;1)存储时用setItem: localStorage.setItem("key","value");//以“key”为名称存储一个值“value”sessionStorage.setItem("key", "value"); &#xff08;2&#xff09;获取时用getItem: localS…

shell学习笔记1-文件安全与权限

1&#xff0c;创建文件的用户和他所属的组拥有该文件&#xff0c;文件的属主可以设定谁具有读、写、执行该文件的权限&#xff0c;根用户可以改变任何普通用户的设置。 2&#xff0c;一个文件一经创建&#xff0c;就具有三种访问权限&#xff1a;读&#xff08;可以显示该文件的…

没有IF-ELSE的工厂

面向对象语言具有非常强大的多态性功能&#xff0c;用于删除代码中的if / else或切换大小写。 没有条件的代码易于阅读。 在某些地方必须放置它们&#xff0c;其中一个示例是Factory / ServiceProvider类。 我敢肯定&#xff0c;您已经看到IF-ELSEIF的工厂课程了&#xff0c;…