src 文件夹中,新建一个 typings 文件夹,用于全局的ts类型声明、模块的ts声明扩展等。
一般tsc会自动查找并解析项目代码中所有的 d.ts 声明文件,符合全局声明的文件,就会直接将类型放入全局中。
对于声明文件的概念此处不进行讲解,可以参考 声明文件 · TypeScript 入门教程

typings结构

  1. ├── business # 业务代码类型声明
  2. ├── user.d.ts
  3. └── xxx.d.ts
  4. ├── shims-fastclick.d.ts # 其他第三方库的扩展声明
  5. ├── shims-tsx.d.ts # vue中支持tsx的扩展声明
  6. └── shims-vue.d.ts # .vue文件扩展声明

d.ts声明 - Vue

shims-tsx.d.ts

Vue Cli 创建ts项目时自带,将其移入typings文件夹中。
该文件允许你以 .tsx 结尾,在vue中使用jsx语法进行开发。

shims-vue.d.ts

Vue Cli 创建ts项目时自带,将其移入typings文件夹中。
ts默认不支持.vue文件,该文件用于告知ts导入 xxx.vue 文件时获得对应的类型检查语法提示。

扩展 Vue.prototype.xxx

在Vue开发过程中,我们经常需要在Vue实例 prototype 上挂载方法。

  1. // main.ts
  2. Vue.prototype.$log = function(){...}
  3. // TodoApp.vue
  4. export default class TodoApp extends Vue {
  5. private mounted() {
  6. this.$log(...) // Property '$log' does not exist on type 'TodoApp'
  7. }
  8. }

如果没有对应的声明文件,我们在实例中调用 this.$toast 时,虽然代码可以正常运行,但是编译器会抛出错误,提示我们 this 上没有 $log 方法。那么我们应该怎么让ts知道我们在Vue实例上挂载了该方法呢?

首先我们得在 main.ts 中给Vue实例挂载方法

  1. // main.ts
  2. Vue.prototype.$log = function (msg: string): void {
  3. console.log(msg)
  4. }

其次就是给Vue实例扩展声明,这里的扩展声明写在 shims-vue.d.ts 中,确保 .vue 文件能够识别。

  1. // shims-vue.d.ts
  2. declare module '*.vue' {
  3. import Vue from 'vue'
  4. export default Vue
  5. // 扩展Vue的prototype,.vue文件中可以用this.$xxx访问并获得语法提示
  6. module 'vue/types/vue' {
  7. interface Vue {
  8. $log: (msg: string) => void
  9. }
  10. }
  11. }

之后在Vue实例中就能正常访问 $log 了,并且能够获得声明文件里的类型校验。

  1. // TodoApp.vue
  2. export default class TodoApp extends Vue {
  3. private mounted() {
  4. this.$log('test') // test
  5. this.$log(1) // Argument of type '1' is not assignable to parameter of type 'string'.
  6. }
  7. }

d.ts声明 - 业务代码

业务代码的ts声明放在 typings/business 文件夹下。

项目中我们肯定会有自己业务上需要的类型定义,一般可以直接在vue实例文件中定义,但是如果一个业务代码的类型可能在项目中各个地方都会用到,就可以放在全局声明一个全局类型了。

例如项目中的用户信息,可能多个地方会使用到该用户的用户信息,我们就应该将用户信息的类型声明到全局:

  1. // typings/business/user.d.ts声明文件中
  2. type User = {
  3. userId: string
  4. userName: string
  5. userStatus: 0 | 1 | 2
  6. }
  7. // 项目.ts中
  8. const user: User = {
  9. userId: 'idxxxxx',
  10. userName: 'namexxxx',
  11. userStatus: 1
  12. }

如果担心类型污染到全局其他类型的声明,可以添加命名空间 namespace

  1. // typings/business/user.d.ts声明文件中
  2. declare namespace User {
  3. type UserInfo = {
  4. userId: string
  5. userName: string
  6. userStatus: 0 | 1 | 2
  7. }
  8. }
  9. // 项目.ts中
  10. const user: User.UserInfo = {
  11. userId: 'idxxxxx',
  12. userName: 'namexxxx',
  13. userStatus: 1
  14. }

d.ts声明 - 第三方库

第三方库的声明分为以下几种情况:

  • 官方自带ts声明,无需额外处理
  • 官方无ts声明,但是社区有在 @types 库中放入对应库的声明文件,需要下载
  • 没有该库的任何ts声明,或者声明有误,需要自己创建/覆盖第三方库的声明
  • 往第三方库上扩展了自定义内容但是没有对应的声明文件,需要手动扩展库的类型声明,比如: Vue.prototype.xxx

    第三方库已有声明 @types

    社区的人帮忙定义好了大部分库的声明文件。一般社区做的第三方库的声明文件都放在 @types 仓库中。
    以jQuery为例,直接npm下载声明文件即可:
    1. npm install @types/jquery --save-dev
    想要寻找更多第三方库的声明,可以在这里查询:Type Search

    创建/覆盖第三方库的声明

    fastclick 为例,该库由于社区给出的声明有错误,所以需要自己创建这个第三方库的声明。
    因此我们要声明该库的类型,要使用 declare module 关键字。
    在 typings 文件夹中,创建一个 shims-fastclick.d.ts 文件: ```typescript // shims-fastclick.d.ts 中 interface FastClickObject { lastTouchIdentifier: number layer: Element tapDelay: number targetElement: HTMLElement touchBoundary: number touchStartX: number touchStartY: number trackingClick: boolean trackingClickStart: number destroy(): void determineEventType(targetElement: HTMLElement): string findControl(labelElement: EventTarget | HTMLLabelElement): HTMLElement focus(targetElement: EventTarget | Element): void getTargetElementFromEventTarget(eventTarget: EventTarget): HTMLElement needsClick(target: EventTarget | Element): boolean needsFocus(target: EventTarget | Element): boolean }

interface FastClickOptions { touchBoundary?: number tapDelay?: number }

interface FastClickStatic { new (layer: HTMLElement | string, options?: FastClickOptions): FastClickObject attach(layer: HTMLElement | string, options?: FastClickOptions): FastClickObject }

declare module ‘fastclick’ { const FastClick: FastClickStatic

export = FastClick }

declare const FastClick: FastClickStatic

// 业务代码中 import FastClick from ‘fastclick’ FastClick.attach(‘xxx’)

  1. <a name="RQpkR"></a>
  2. ## 扩展第三方库的声明
  3. 扩展第三方库声明时,声明文件需要先引入这个库:
  4. ```typescript
  5. // typings/libs/dayjs.d.ts
  6. import dayjs from 'dayjs'
  7. declare module 'dayjs' {
  8. export function testDayjs(): dayjs.Dayjs
  9. }

使用时,使用的地方也得导入这个声明文件:

  1. import { Component, Vue } from 'vue-property-decorator'
  2. import dayjs from 'dayjs'
  3. import '@/typings/libs/dayjs'
  4. @Component
  5. export default class TodoApp extends Vue {
  6. private mounted() {
  7. dayjs.testDayjs()
  8. }
  9. }

d.ts声明 - 直接声明全局变量、类型等

有时候可能会在项目中声明一些全局变量,如下:

  • declare var 声明全局变量
  • declare function 声明全局方法
  • declare class 声明全局类
  • declare enum 声明全局枚举类型
  • declare namespace 声明(含有子属性的)全局对象
  • interfacetype 声明全局类型

    d.ts声明 - 扩展全局变量

    有些第三方库扩展了全局变量,比如直接给字符串添加了某些方法,但是却没有对应的声明导致类型检查报错,此时可以直接扩展全局变量的声明: ```typescript interface String { prependHello(): string; }

‘foo’.prependHello();

  1. 通过声明合并,给 `String` 添加全局方法。<br />也可以使用命名空间 `namespace` 给已有的命名空间添加类型声明:
  2. ```typescript
  3. declare namespace JQuery {
  4. interface CustomOptions {
  5. bar: string;
  6. }
  7. }
  8. interface JQueryStatic {
  9. foo(options: JQuery.CustomOptions): string;
  10. }
  1. // src/main.ts
  2. jQuery.foo({
  3. bar: ''
  4. });