当前位置: 首页 > Web前端 > HTML

不知道如何在TypeScript中查找类型定义,看这里

时间:2023-03-28 10:45:28 HTML

1.前言TypeScript已经成为前端开发者的必备技能。既可以提升开发者的开发体验,又可以保证项目的可维护性,这主要得益于TypeScript的静态检测机制,可以帮助我们在编译时发现错误。当然,如果你还信什么大法,就当我什么都没说。当然,今天的话题不是介绍TypeScript的语法和用法。今天介绍的主要特点是它如何帮助我们找到我们定义的类型。这也是笔者最近遇到的一个问题。我先列几个问题。想一想:node_modules中的@types文件夹有什么作用?TypeScript是怎么找到他的?tsconfig中的typeRoots和types属性有什么作用?它们与类型查找有什么关系?tsconfig中的文件、包含和排除属性有什么作用?它们与类型查找有什么关系?TypeScript声明文件有两种类型:全局库和模块化库?他们有什么特点?如何定制?如果你能成功回答所有的问题,那么恭喜你,你已经成功通过了TypeScript的类型定义和查询规则。2、配置开发环境其实一般来说,我们项目中的类型文件分为两种,一种是第三方库定义的类型,一种是我们自己项目中自定义的类型。1、2这两个问题属于第一类,3、4属于第二类。下面将通过具体的例子对它们分别进行说明。首先,配置我们的开发环境。对于第三方库,我们来分析两个有代表性的库,一个是Jquery,一个是ahooks,是阿里开发的一个通用的hooks库。这里就不介绍了:使用create-react-app创建TypeScript项目npxcreate-react-appmy-app--templatetypescript#oryarncreatereact-appmy-app--templatetypescriptinstalljqueryandahooksnpmi-Djqueryahooks#oryarnadd-Djqueryahooks3.第三方库1.jquery首先在src文件夹下新建一个test.ts文件,因为我们已经安装好了现在jquery我们可以直接使用全局变量$,我们试试看:当我们输入$的时候,我们会发现编辑器既没有语法提示也没有错误,然后提示我们安装@types/jquery包,这是我们和@的第一次见面类型,让我们先安装它们。安装好后我们再试一下,发现一切都是那么美好,为什么这里会出现这样的问题呢?事实上,TypeScript并没有找到$的定义。如果我们直接无视(禁止ts提示),直接运行,可以发现项目还是可以直接运行的,因为我们安装了webpack这个包,当然可以找到这个包,不会有问题.回到主线程,我们到node_module目录下的@types目录下看一看:果然,他在这个目录下安装了jquery的定义文件。至于如何找到TypeScript,我们以后再说。2、ahooks下,在index.ts文件中导入ahooks,使用里面的useUpdateEffecthook:import{useUpdateEffect}from'ahooks'constTest=()=>{useUpdateEffect(()=>{console.log()},[])}这里我们发现编辑器并没有报错,也能正常显示useUpdateEffecthook的类型定义,也就是说我们不用导入对应的类型文件TypeScript就可以找到它对应的类型定义。3.总结这里对上面提到的两个库做一个总结。首先说一下@types目录的作用。它其实是用来保存一些不用TypeScript写的库的类型定义文件的,像jquery是用js写的,ahooks是用ts写的。那么TypeScript究竟是如何找到这些类型的呢?这必须与两个属性typeRoots和types相关联。首先要说的是,第三方库的类型查找规则和node的包查找规则类似。首先,你会在当前文件夹下找到node_modules,在他下面递归查找,如果没有找到,就去上层目录找到node_modules目录,然后递归查找,直到根目录。但是,类型搜索仍然存在一些差异。我们以jquery库为例:找到当前文件下的node_modules目录,发现走道上一层的node_modules目录没有找到。找到根目录下的node_modules目录,递归查找jquery包,发现有找到jquery包。package.json查找types属性,没有,说明这个库是js写的。去@types找他的类型定义,找到jquery类型定义包,然后去他的package.json文件,搜索types属性,发现存在,找到types属性指定的文件(index.d.ts),这个文件是jquery类型定义的入口(如果你不知道.d.ts和.ts文件的区别,请点击这里)。它可以分三步完成。我们看ahooks的package.json:显示其类型定义的入口在这个包的lib目录下。至此,大家应该对ts的类型查找有了一定的了解,但是可能会有同学会问,刚才是不是提到了typeRoots和types这两个属性?它们的功能是什么?typeRoots和types都是tsconfig.conf中compilerOptions的配置项。它们下面的包将被ts编译器自动包含。typeRoots默认指向node_modules/@types,这就解释了为什么ts会去@types寻找类型定义文件,因为这说明我们也可以手动调整ts的搜索路径,比如我们开发了一个通用的组件库withts本地,其类型定义文件存放在typings目录下,那么我们可以这样修改:"typeRoots":["node_modules/@types","./typings"]types的作用是什么?如果不想自动导入typeRoots指定路径下的所有声明模块,可以使用types指定自动导入哪些模块。例如:{"compilerOptions":{"types":["node","lodash","express"]}}那么只会导入node、lodash、express这三个声明模块,其他声明模块不会导入自动导入。4.自定义类型声明自定义类型声明有两种,一种是全局库,一种是模块化库。它与SCMAScript2015相同。任何包含顶级import或export的文件都将被视为一个模块。相反,如果没有顶级导入或导出声明的文件的内容被认为是全局可见的。1.全局库全局库是指可以在全局命名空间中访问。接下来,我们在根目录下创建一个global.d.ts文件:declareconstfuc:(a:number,b:number)=>numberdeclarevarn:numberdeclaretypeA={name:string,age:number}这里我们通过declare声明了几个全局变量。这里要注意,此时不能全局使用。我们需要在tsconfig文件中配置:"include":["./**/*",]把我们的include属性改成这个,至于为什么要改这个后面会说到,然后我们就可以使用我们的变量了定义在上面的任何地方,比如我们在index.js文件中输入fuc是这样的:编辑器会提示你输入这个函数,并给出它的类型定义。2.模块化库模块化类型库应该是我们开发过程中用的最多的了。例如,我们将在单独的.ts文件中定义一些公共类型,然后将它们导出:exportconsta:number=1exportconstadd=(x:number,y:number)=>x+yexportinterfaceUser{name:string,department:string}当然我们也可以不在导出模块中定义变量的类型,而是在导入模块中进行导入变量的类型定义://src/person.tsexportclassPerson{}//src.index.tsimport{Person}from'./person';declaremodule'./person'{interfacePerson{greet:()=>void;}}Person.prototype.greet=()=>{console.log('嗨!');};我们在person.ts中定义了一个Person类,在index.ts中引入了这个类通过declaremodule将'./person'声明为一个模块,并在这个模块下的Person中添加相应的类型声明,这里是添加一个方法到Person类的原型,这样我们就可以在原型上正常使用了。也可以使用第三方库中的模块化库,比如ahooks中的createUpdateEffect。让我们看看他的.d.ts文件定义:importtype{useEffect,useLayoutEffect}from'react';声明类型effectHookType=typeofuseEffect|typeofuseLayoutEffect;exportdeclareconstcreateUpdateEffect:(hook:effectHookType)=>effectHookType;export{};然后将其导入到lib/index.d.ts中。3.总结以上主要介绍了全局库和模块化库的一些使用。这里主要说一下文件的属性,include和exclude,它们的作用是什么。我们首先要了解ts文件是一种什么样的文件?答案如下:对于扩展名为.ts、.tsx、.d.ts的文件,如果我们设置allowJs=true,那么.js和jsx也会被视为ts文件。接下来files、include、exclude这三个属性是控制ts编译器的编译范围的。它们各自的特点如下:files是一个数组,数组的元素可以是相对路径也可以是绝对路径。include和exclude属性是一个数组,但是组的元素类似于glob文件模式,比如*,?,**/通配符,如果同时设置了files和include,那么编译器会导入两者所指定的文件,exclude将只对include有效,对files无效,即files指定的文件如果同时被exclude排除,文件还是会被编译器引入。所以在导入global.d.ts的时候,需要配置include属性,将其带入编译范围,然后我们定义的变量才会被找到。