在 src
文件夹中,新建一个 typings
文件夹,用于全局的ts类型声明、模块的ts声明扩展等。
一般tsc会自动查找并解析项目代码中所有的 d.ts
声明文件,符合全局声明的文件,就会直接将类型放入全局中。
对于声明文件的概念此处不进行讲解,可以参考 声明文件 · TypeScript 入门教程
typings结构
├── business # 业务代码类型声明
│ ├── user.d.ts
│ └── xxx.d.ts
│
├── shims-fastclick.d.ts # 其他第三方库的扩展声明
├── shims-tsx.d.ts # vue中支持tsx的扩展声明
└── 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
上挂载方法。
// main.ts
Vue.prototype.$log = function(){...}
// TodoApp.vue
export default class TodoApp extends Vue {
private mounted() {
this.$log(...) // Property '$log' does not exist on type 'TodoApp'
}
}
如果没有对应的声明文件,我们在实例中调用 this.$toast
时,虽然代码可以正常运行,但是编译器会抛出错误,提示我们 this
上没有 $log
方法。那么我们应该怎么让ts知道我们在Vue实例上挂载了该方法呢?
首先我们得在 main.ts
中给Vue实例挂载方法
// main.ts
Vue.prototype.$log = function (msg: string): void {
console.log(msg)
}
其次就是给Vue实例扩展声明,这里的扩展声明写在 shims-vue.d.ts
中,确保 .vue 文件能够识别。
// shims-vue.d.ts
declare module '*.vue' {
import Vue from 'vue'
export default Vue
// 扩展Vue的prototype,.vue文件中可以用this.$xxx访问并获得语法提示
module 'vue/types/vue' {
interface Vue {
$log: (msg: string) => void
}
}
}
之后在Vue实例中就能正常访问 $log
了,并且能够获得声明文件里的类型校验。
// TodoApp.vue
export default class TodoApp extends Vue {
private mounted() {
this.$log('test') // test
this.$log(1) // Argument of type '1' is not assignable to parameter of type 'string'.
}
}
d.ts声明 - 业务代码
业务代码的ts声明放在 typings/business 文件夹下。
项目中我们肯定会有自己业务上需要的类型定义,一般可以直接在vue实例文件中定义,但是如果一个业务代码的类型可能在项目中各个地方都会用到,就可以放在全局声明一个全局类型了。
例如项目中的用户信息,可能多个地方会使用到该用户的用户信息,我们就应该将用户信息的类型声明到全局:
// typings/business/user.d.ts声明文件中
type User = {
userId: string
userName: string
userStatus: 0 | 1 | 2
}
// 项目.ts中
const user: User = {
userId: 'idxxxxx',
userName: 'namexxxx',
userStatus: 1
}
如果担心类型污染到全局其他类型的声明,可以添加命名空间 namespace
。
// typings/business/user.d.ts声明文件中
declare namespace User {
type UserInfo = {
userId: string
userName: string
userStatus: 0 | 1 | 2
}
}
// 项目.ts中
const user: User.UserInfo = {
userId: 'idxxxxx',
userName: 'namexxxx',
userStatus: 1
}
d.ts声明 - 第三方库
第三方库的声明分为以下几种情况:
- 官方自带ts声明,无需额外处理
- 官方无ts声明,但是社区有在
@types
库中放入对应库的声明文件,需要下载 - 没有该库的任何ts声明,或者声明有误,需要自己创建/覆盖第三方库的声明
- 往第三方库上扩展了自定义内容但是没有对应的声明文件,需要手动扩展库的类型声明,比如:
Vue.prototype.xxx
第三方库已有声明 @types
社区的人帮忙定义好了大部分库的声明文件。一般社区做的第三方库的声明文件都放在@types
仓库中。
以jQuery为例,直接npm下载声明文件即可:
想要寻找更多第三方库的声明,可以在这里查询:Type Searchnpm install @types/jquery --save-dev
创建/覆盖第三方库的声明
以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’)
<a name="RQpkR"></a>
## 扩展第三方库的声明
扩展第三方库声明时,声明文件需要先引入这个库:
```typescript
// typings/libs/dayjs.d.ts
import dayjs from 'dayjs'
declare module 'dayjs' {
export function testDayjs(): dayjs.Dayjs
}
使用时,使用的地方也得导入这个声明文件:
import { Component, Vue } from 'vue-property-decorator'
import dayjs from 'dayjs'
import '@/typings/libs/dayjs'
@Component
export default class TodoApp extends Vue {
private mounted() {
dayjs.testDayjs()
}
}
d.ts声明 - 直接声明全局变量、类型等
有时候可能会在项目中声明一些全局变量,如下:
declare var
声明全局变量declare function
声明全局方法declare class
声明全局类declare enum
声明全局枚举类型declare namespace
声明(含有子属性的)全局对象interface
和type
声明全局类型d.ts声明 - 扩展全局变量
有些第三方库扩展了全局变量,比如直接给字符串添加了某些方法,但是却没有对应的声明导致类型检查报错,此时可以直接扩展全局变量的声明: ```typescript interface String { prependHello(): string; }
‘foo’.prependHello();
通过声明合并,给 `String` 添加全局方法。<br />也可以使用命名空间 `namespace` 给已有的命名空间添加类型声明:
```typescript
declare namespace JQuery {
interface CustomOptions {
bar: string;
}
}
interface JQueryStatic {
foo(options: JQuery.CustomOptions): string;
}
// src/main.ts
jQuery.foo({
bar: ''
});