.d.ts文件类型声明、shims-vue.d.ts文件、defineComponent作用、.browserslistrc文件、tsconfig.json文件

312 阅读6分钟

.d.ts文件类型声明的查找

之前我们所有的typescript中的类型,几乎都是我们自己编写的,但是我们也有用到一些其他的类型:

大家是否会奇怪,我们的HTMLImageElement类型来自哪里呢?甚至是document为什么可以有getElementById的方法呢?其实这里就涉及到typescript对类型的管理和查找规则了。

我们这里先给大家介绍另外的一种typescript文件:.d.ts文件(.declare.js的简写)。 我们之前编写的typescript文件都是 .ts 文件,这些文件最终会输出 .js 文件,也是我们通常编写代码的地方,还有另外一种 .d.ts 文件,它是用来做类型的声明(declare)。 它仅仅用来做类型检测,告知typescript我们有哪些类型。

那么typescript会在哪里查找我们的类型声明呢?

  1. 内置类型声明;
  2. 外部类型声明;比如第三方库axios,就是自带类型声明的,也就是自带.d.ts文件。但是lodash没有自带.d.ts文件,所以不能直接使用。
  3. 自定义类型声明;

内置类型声明

内置类型声明是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进行转换的。