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原生模块写法,并且导出了属性和方法leta =1;functiongeta(){returna;}functionseta(val){a = val}export {geta, seta, a,default: a}


// 为simple/index.js写全局类型声明,在types.d.ts中添加如下代码declare module"simple"{  let a: number;  exportfunctiongeta(): void;exportfunctionseta(n: number): void;exportdefaulta;}


// app.ts  使用三斜线指令引入声明文件///import a, {geta, seta}from'simple'console.log(a)


// /node_modules/simple/lib1.js// 导出一个类functionAb(){this.a =1}Ab.prototype.seta =function(num){this.a = num}Ab.prototype.geta =function(num){returnthis.a}exports.Ab = Ab


// 在type.d.ts文件中添加declare module"simple/lib1"{  exportclassAb {    private a;    seta(n: number):void;    geta(): number  }}


// app.ts  使用三斜线指令引入声明文件/// importa, {geta, seta}from'simple'import {Ab}from'simple/lib1'// 得以验证console.log(newAb())console.log(a)


// /node_modules/simple/lib2.js// 只导出一个函数module.exports =functiongetRandom(){returnMath.random()}


//在type.d.ts文件中添加declaremodule"simple/lib2"{letgetRandom:()=>number;export= getRandom;}


// app.ts  使用三斜线指令引入声明文件/// import a, {geta, seta} from'simple'import {Ab} from'simple/lib1'import getRandom =require('simple/lib2')// 得以验证console.log(getRandom())console.log(newAb())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.tslet a: number;exportfunctiongeta(): void;exportfunctionseta(n: number): void;exportdefaulta;


// app.tsimporta, {geta, seta}from'simple'// 得以验证console.log(geta())


//lib1.d.tsfunctionAb(){this.a =1}Ab.prototype.seta =function(num){this.a = num}Ab.prototype.geta =function(){returnthis.a}exports.Ab = Ab


importa, {geta, seta}from'simple'import {Ab}from'simple/lib1'// 得以验证console.log(newAb().geta())console.log(geta())


//lib2.d.tsletgetRandom:()=>number;export= getRandom;


importa, {geta, seta}from'simple'import {Ab}from'simple/lib1'import getRandom =require('simple/lib2')// 得以验证console.log(getRandom())console.log(newAb().geta())console.log(geta())


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

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

// types.d.ts  替换simple声明如下decalremodule"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有两种写法

exportconsta: number;// 只要导出即可


declaremodule"abc"{exportconsta: 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是一样的,然后对于全局的声明文件还在需要的时候使用 ///  引用进来。所以我怀疑(因为我还没有了解到是不是事实)import … from "name" 这个其实引用的是我们在声明文件中定义的module。

什么是module?

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

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

全局类型声明的思考

全局类型声明中只是声明了相关模块当然也可以声明其他东西,而是用全局类型声明的方法,不是import(这不是一个模块)而是三斜线指令使用 ///  这样 .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模块解析


原文引用自:https://blog.csdn.net/letterTiger/article/details/80596369

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,513评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,312评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,124评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,529评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,937评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,913评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,084评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,816评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,593评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,788评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,267评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,601评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,265评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,158评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,953评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,066评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,852评论 2 277

推荐阅读更多精彩内容