.d.ts文件类型声明的查找
之前我们所有的typescript中的类型,几乎都是我们自己编写的,但是我们也有用到一些其他的类型:
大家是否会奇怪,我们的HTMLImageElement类型来自哪里呢?甚至是document为什么可以有getElementById的方法呢?其实这里就涉及到typescript对类型的管理和查找规则了。
我们这里先给大家介绍另外的一种typescript文件:.d.ts文件
(.declare.js的简写)。
我们之前编写的typescript文件都是 .ts
文件,这些文件最终会输出 .js
文件,也是我们通常编写代码的地方,还有另外一种 .d.ts 文件
,它是用来做类型的声明(declare)。 它仅仅用来做类型检测,告知typescript我们有哪些类型。
那么typescript会在哪里查找我们的类型声明呢?
- 内置类型声明;
- 外部类型声明;比如第三方库axios,就是自带类型声明的,也就是自带
.d.ts文件
。但是lodash没有自带.d.ts文件
,所以不能直接使用。 - 自定义类型声明;
内置类型声明
内置类型声明是typescript自带的,它帮助我们内置了JavaScript运行时的一些标准化API的声明文件,包括比如Math、Date等内置类型,也包括DOM API,比如Window、Document等。内置类型声明通常在我们安装typescript的环境中会带有的:github.com/microsoft/T…
外部类型声明
外部类型声明通常是我们使用一些库(比如第三方库)时,需要的一些类型声明。这些库通常有两种类型声明方式:
- 方式一:在自己库中进行类型声明(编写.d.ts文件),比如axios。
- 方式二:通过社区的一个公有库DefinitelyTyped存放类型声明文件。
✅该库的GitHub地址:github.com/DefinitelyT…
✅该库查找声明安装方式的地址:www.typescriptlang.org/dt/search?s…
✅比如我们安装react的类型声明:npm i @types/react --save-dev
自定义类型声明
什么情况下需要我们来自定义类型声明呢?
- 情况一:我们使用的第三方库是一个纯的JavaScript库,没有对应的声明文件。
- 情况二:我们给自己的代码中声明一些类型,方便在其他地方直接进行使用。
① 声明模块
虽然公有库DefinitelyTyped里面已经给lodash声明类型了,我们假设它没有给lodash声明类型,我们就需要自己自定义类型声明。我们新建coderwhy.d.ts文件
,代码如下:
// 给lodash声明模块
declare module 'lodash' {
// 声明模块里的方法
export function join(arr: any[]): void
}
声明模块和模块里的方法以后,我们通过如下方式使用lodash就不会报错了。
import lodash from 'lodash'
console.log(lodash.join(["abc", "cba"]))
② 声明变量、函数、类
当我们在index.html中的script标签中写如下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 变量
let whyName = "coderwhy"
let whyAge = 18
let whyHeight = 1.88
// 函数
function whyFoo() {
console.log("whyFoo")
}
// ES5定义类就是这样定义的
function Person(name, age) {
this.name = name
this.age = age
}
</script>
</body>
</html>
然后在main.ts中使用:
console.log(whyName)
console.log(whyAge)
console.log(whyHeight)
whyFoo()
const p = new Person("why", 18)
console.log(p)
按照js代码执行的顺序,上面代码应该不会报错,但是在TypeScript中,上面代码不能通过TypeScript的类型约束,因为TypeScript不知道上面那些变量、函数、类是什么,所以我们需要声明一下,在coderwhy.d.ts文件
中新增如下代码:
// 声明变量
declare let whyName: string
declare let whyAge: number
declare let whyHeight: number
// 声明函数
declare function whyFoo(): void
// 声明类
declare class Person {
name: string
age: number
constructor(name: string, age: number)
}
这样代码就不会报错了。
③ 声明文件
在某些情况下,我们也可以声明文件。比如在开发vue的过程中,默认是不识别我们的.vue文件的,那么我们就需要对其进行文件的声明。比如在开发中我们使用了 jpg 这类图片文件,默认typescript也是不支持的,也需要对其进行声明。
// 声明文件
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.png'
declare module '*.svg'
declare module '*.gif'
声明之后通过import nhltImage from './img/nhlt.jpg'
,引入图片就不会报错了。
④ 声明命名空间
比如我们在index.html中通过script标签引入了jQuery,这时候我们就可以在index.html中通过$.ajax使用了,但是无法在main.ts中使用,这时候我们可以声明命名空间,如下:
// 声明命名空间
declare namespace $ {
export function ajax(settings: any): any
}
声明之后在main.ts中就可以使用了:
shims-vue.d.ts文件
当我们加载一些比较特殊的文件,比如.vue文件,其实默认TS是不认识这些文件的,就会报错。但是我们在main.ts里面使用App.vue文件是不会报错的, 这是因为当前项目是通过脚手架创建的,也就是说这些配置默认都给我们配好了,其实这些文件的类型声明就是在shims-vue.d.ts文件里面。
shims-vue.d.ts文件:
/* eslint-disable */
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
declare let $store: any
defineComponent()作用
当我们在.vue文件中使用TS,代码如下:
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'App',
props: {
name: {
type: String
}
}
})
</script>
和原来相比多了lang="ts",代表我们使用TS编写代码,还有多了defineComponent()函数进行包裹,其实defineComponent的作用就是类型限制和类型推导的。
.browserslistrc文件
Babel等插件都会读取这个文件进行浏览器适配。
> 1% //适配市场份额大于1%的浏览器
last 2 versions //适配主流浏览器最新的两个版本
not dead //适配没有死的浏览器,如果12个月内有更新,那就是没有死
tsconfig.json文件
如果项目中有使用TS,就会有这个文件,用来做TS相关的配置。从TS转成JS的时候需要一些配置,这些配置就是tsconfig.json文件。
{
//编译选项
"compilerOptions": {
// 目标代码(ts -> js(es5/6/7))
"target": "esnext",
// 目标代码需要使用的模块化方案(commonjs:require、module.exports es module:import、export)
// 如果设置为umd就是支持多种模块化
"module": "esnext",
// 打开一些严格的检查
"strict": true,
// 对jsx进行怎么样的处理 preserve保留,不转换
"jsx": "preserve",
// 导入辅助功能
"importHelpers": true,
// 按照node的方式去解析模块 import "/index.node"
"moduleResolution": "node",
// 跳过三方库的类型检测 (axios -> 类型/ lodash -> @types/lodash / 其他的第三方)
// import { Person } from 'axios'
"skipLibCheck": true,
// es module 和 commonjs能不能混合来使用,下面俩属性一般是一起的
// export default/module.exports = {}
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
// 要不要生成映射文件(ts -> js)
"sourceMap": true,
// 文件路径在解析时的基本url, 默认是以当前文件为准
"baseUrl": ".",
// 指定具体要解析使用的类型
"types": ["webpack-env"],
// 路径解析(类似于webpack alias)
"paths": {
"@/*": ["src/*"],
"components/*": ["src/components/*"]
},
// 指定在项目中可以使用哪里库的类型(Proxy/Window/Document)
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
},
// 哪些ts代码需要进行编译解析
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
// 哪些ts代码不需要进行编译解析
// 比如当我们导入axios的时候,其实axios就是在node_modules里面的
// 如果我们把它排除,就是引入的库的在node_modules里面的代码不进行解析,但是我们自己写的关于axios的代码还是会被解析
"exclude": ["node_modules"]
}
这个文件的配置有个大概的了解就可以,因为我们创建项目的时候有选择使用Babel,所以最终我们都会通过Babel进行转换的。