乐趣区

关于前端:还不知道-TypeScript-怎么去查找类型定义的看这里

一、前言

TypeScript 现在曾经成为前端开发者必备的一项技能,它不经可能晋升开发者的开发体验还能保障我的项目的可维护性,这次要得益于 TypeScript 的动态检测机制,能够帮忙咱们在编译的时候发现错误,当然如果你还是一个 any 大法的信仰者就当我没说。

当然明天的主题当然不是去介绍 TypeScript 的语法以及用法,明天重点介绍的配角是它如何去帮咱们找到咱们定义的 类型,这也是笔者最近遇到的问题,上面我先列举几个问题,大家先思考下:

  1. node_modules 中 @types 文件夹有什么作用?TypeScript 是怎么找到他的?
  2. tsconfig 中的 typeRootstypes 属性有什么作用?他们和类型查找有什么关系?
  3. tsconfig 中的filesincludeexclude 属性有什么作用?他们和类型查找有什么关系?
  4. TypeScript 申明文件有 全局库 模块化库 两种?他们的特点是什么?怎么去自定义?

如果你都能顺利的答,那么祝贺你 TypeScript 的类型定义以及查问规定这一块你曾经顺利过关。

二、配置开发环境

其实总的来说在咱们的我的项目中类型文件分为两种,一个是第三方库定义好的类型,一个就是本人我的项目中自定义的类型,1、2 这两个问题属于第一类、3、4 属于第二类,上面通过具体的实例别离解释,首先先配置好咱们的开发环境,对于第三方库咱们拿两个比拟有代表性的库来剖析,一个是 Jquery,一个是 ahooks 这是阿里开发的一个通用的 hooks 库,具体在这就不做过多介绍:

  1. 应用 create-react-app 创立 TypeScript 我的项目
npx create-react-app my-app --template typescript
#or
yarn create react-app my-app --template typescript
  1. 装置 jquery 和 ahooks
npm i -D jquery ahooks
#or
yarn add -D jquery ahooks

三、第三方库

1、Jquery

首先在 src 文件夹下新建一个 test.ts 文件,因为咱们曾经装了 jquery 咱们能够间接应用全局变量 $ 了,来试试:


当咱们输出 $ 后咱们会发现编辑器既没有语法提醒还呈现报错,而后提醒咱们去装置 @types/jquery 这个包,这是咱们和 @types 的首次见面,咱们先去装置。


装置好之后咱们再试试发现一切都是那么的美妙,这里为什么呈现这样的问题呢?其实就是 TypeScript 没有找到 $ 的定义, 如果咱们间接漠视它 (禁止 ts 提醒) 而后间接运行发现我的项目还是能够间接运行起来的,因为咱们装了这个包 webpack 当然就能找到这个包就没问题了呀。
话题回到主线上,此时咱们去 node_module 目录下的 @types 目录下看一下:


果然他将 jquery 的定义文件装置到这个目录下,至于 TypeScript 是怎么找到的咱们前面会说。

2、ahooks

上面还是在 index.ts 文件上来引入 ahooks 并应用了外面的 useUpdateEffect 这个 hook:

import {useUpdateEffect} from 'ahooks'
const Test = () => {useUpdateEffect(() => {console.log()
    },[])
}


这里咱们发现编辑器并没有报错,同时也能失常展现出 useUpdateEffect 这个 hook 的类型定义,也就是说咱们不须要引入对应的类型文件 TypeScript 就能找到他对应的类型定义。

3、总结

这里对于下面讲的两个库做一下总结,首先说一下 @types 这个目录的作用,它其实就是用来保留一些没有应用的 TypeScript 编写的库的类型定义文件,像 jquery 他就是用 js 编写的,而 ahooks 就是应用 ts 编写的。

那 TypeScript 到底是怎么找到这些类型的呢?这就得和 typeRootstypes 这两个属性关联起来了,首先要说的是其实第三方库的类型查找的规定和 node 的包查找是相似的,首先会在以后文件夹找 node_modules, 在他上面递归的去查找,如果找不到就会去下层的目录找到 node_modules 目录,再递归查找,直到根目录。然而对于类型查找还是有些差异,上面拿 jquery 这个库来说:

  1. 在以后文件下找 node_modules 目录,发现没找到走道上一层
  2. 在根目录下找到 node_modules 目录,递归查找 jquery 包,发现存在
  3. 找到 jquery 包的 package.json 查找 types 属性发现没有,阐明这个库是 js 编写的,去 @types 找他的类型定义
  4. 找到 jquery 的类型定义包,而后去找他的 package.json 文件并查找 types 属性,发现存在
  5. 找到 types 属性指定的文件(index.d.ts),这个文件就是 jquery 类型定义的入口(如果还不晓得.d.ts 和.ts 文件的区别点击这里)

如果是应用 ts 编写的库如 ahooks 那么在第三步就能够完结,咱们看下 ahooks 的 package.json:


阐明他的类型定义的入口就在这个包的 lib 目录下,至此大家对 ts 的类型查找应该有了肯定的理解,然而可能就有同学会问了,你刚刚不是说的 typeRootstypes 这两个属性呢,他们的作用是啥?

typeRootstypes 都是 tsconfig 中 compilerOptions 的一个配置项,他们上面的包会被 ts 编译器主动蕴含进来,typeRoots 默认指向 node_modules/@types,这也就阐明了为什么 ts 会去 @types 上面去找类型定义文件,既然这样阐明咱们也能够手动去调整 ts 的查找门路,比方咱们本地用 ts 开发了一个通用的组件库,他的类型定义文件保留在 typings 目录下,那咱们能够这么批改:

 "typeRoots": ["node_modules/@types","./typings"]

types 有什么作用呢?如果不心愿主动引入 typeRoots 指定门路下的所有申明模块,那能够应用 types 指定主动引入哪些模块。比方:

{
  "compilerOptions": {"types" : ["node", "lodash", "express"]
  }
}

那么就只会引入 node、lodash、express 这三个申明模块,其它的申明模块则不会被主动引入。

四、自定义类型申明

自定义类型申明也分为两种,一个是 全局库 、一个是 模块化库,它和 SCMAScript 2015 一样,任何蕴含顶级 import 或者 export 的文件都会被当成一个模块,相同,如果一个文件不带有顶级的import 或者 export 申明,那么它的内容被视为全局可见。

1、全局库

全局库就是指能在全局命名空间下拜访,上面咱们在根目录下创立一个 global.d.ts 文件:

declare const fuc:(a:number,b:number) => number
declare var n:number
declare type A = {
    name:string,
    age:number
}

在这外面咱们通过 declare 申明了几个全局的变量,这里要留神了此时还是不能全局应用的,咱们要去 tsconfig 文件下做一下配置:

"include": ["./**/*",]

将咱们的 include 属性改成这样,至于为什么这么改前面会讲到,而后咱们就能在任何中央去应用咱们下面定义的变量, 比方咱们在 index.js 文件下这样输出 fuc:


发现编辑器给你提醒了这个函数,并给出了他的类型定义。

2、模块化库

模块化类型库应该是咱们开发过程中用的最多的,比方咱们会在独自的一个.ts 文件中定义一些公共的类型而后导出:

export const a: number = 1

export const add = (x: number, y:number) => x + y 

export interface User {
  name: string,
  department: string
}

当然咱们在导出模块也能够不去定义变量的类型,而在引入的模块中对引入的变量进行类型定义,如下所示:

//src/person.ts
export class Person {}

//src.index.ts
import {Person} from './person';
declare module './person' {
  interface Person {greet: () => void;
  }
}
Person.prototype.greet = () => {console.log('Hi!');
};

咱们在 person.ts 中之定义了一个 Person 类,在 index.ts 中引入这个类通过 declare module 将 ‘./person‘申明为一个模块,并对这个模块上面的 Person 减少对应的类型申明,这里就是给 Person 类的原型上减少了一个办法,这样咱们就能失常的在原型上应用这个办法。

在第三方库中也能够去应用模块化库,比方 ahooks 中的 createUpdateEffect,我么看下他的 .d.ts 文件定义:

import type {useEffect, useLayoutEffect} from 'react';
declare type effectHookType = typeof useEffect | typeof useLayoutEffect;
export declare const createUpdateEffect: (hook: effectHookType) => effectHookType;
export {};

而后在 lib/index.d.ts 中通过 import 导入。

3、总结

下面次要介绍了 全局库 模块化库 的一些应用,这里次要说一下 filesincludeexclude属性,他们有什么作用。

咱们首先要理解下什么样的文件才算是 ts 文件呢?答案如下:

  1. 拓展名为.ts、.tsx、.d.ts 的文件
  2. 如果咱们设置了 allowJs = true,那么.js、jsx 也会被视为 ts 文件

接着说 filesincludeexclude这三个属性是管制着 ts 编译器的编译范畴,他们各自的特点如下:

  1. files 是一个数组,数组的元素能够是相对路径和绝对路径
  2. inclue 和 exclude 属性是一个数组,然而组的元素相似 glob 的文件模式,比方 *?**/ 这种通配符的模式
  3. 如果同时设置了 filesinclude,那么编译器会把两者指定的文件引入,而 exclude 只会对 include 无效,对 files是有效的,即 files 指定的文件如果同时被 exclude 排除,那么该文件依然会被编译器引入。

所以在引入 global.d.ts 时须要去配置 include属性把它纳入编译范畴之内,此时咱们定义的变量才会被找到。

退出移动版