//service.d.ts interface createAccountParams { accountType?: string, username: string, password: string, gender: 'X' | 'F' | 'M', email: string, level?: number } interface createAccountReturn { userId: string, } export interface Service { createAccount(data: createAccountParams): createAccountReturn }
这样一个service层的接口文件的声明文件就编写完成了,为了获得Typescript和vscode提供的类型提示和校验,在main.js中将service.js导出的实例绑定在了Vue原型上,使得我们可以在vue组件中通过vm.$service方便的访问service实例。但是Typescript并不知道Vue实例上有什么属性,这时需要我们在之前添加的shims-vue.d.ts文件中添加几行代码。
import Vue from 'vue'; import Service from "pathToService/service.d.ts"; declare module '*.vue' { export default Vue; } declare module "vue/types/vue" { interface Vue { $service: Service } }
得力于typescript中提供的模块补充功能,我们可以在node_modules/vue/types/vue中补充我们需要在Vue上提供的属性。
改写Vue文件
我们需要将原来的vue文件改写成使用vue-property-decorator编写的方式。
<script lang="ts"> import {Component,Vue} from "vue-property-decorator"; @Component export default class MyComponent extends Vue{ // 声明data form: { accountType?: string, username: string, password: string, gender: 'X' | 'F' | 'M', email: string, level?: number } = { username:'', password:'', gender:'X', email:'' }; // 声明methods async submit(): void { //调用上面的service接口 const [res,err] = await this.$service.createAccount(this.form); } } </script>
至此一个Vue项目迁移至Typescript的过程就已经完成了,剩下的工作就是将代码中其他的文件一步步由js迁移到typescript中。
把方法绑定到Vue实例下
除了我们之前提到过的将自己编写的service挂载到vue实例上,大家一定清楚在vue项目中,我们经常会调用this.$refs this.$router this.$store等等,typescript也会检查这些属性是否被绑定在了vue实例上,那么我们并没有在类型系统中声明这些值,按道理应该报Property '$refs' does not exist on type [your_component_name]
真相是vue vue-router vuex都已经给我们声明了相应的类型,我们可以cd ./node_modules/vue/types/目录中去查看
截取少量代码如下所示:
export interface Vue { readonly $el: Element; readonly $options: ComponentOptions<Vue>; readonly $parent: Vue; readonly $root: Vue; readonly $children: Vue[]; readonly $refs: { [key: string]: Vue | Element | Vue[] | Element[] }; readonly $slots: { [key: string]: VNode[] | undefined }; readonly $scopedSlots: { [key: string]: NormalizedScopedSlot | undefined }; readonly $isServer: boolean; readonly $data: Record<string, any>; readonly $props: Record<string, any>; readonly $ssrContext: any; readonly $vnode: VNode; readonly $attrs: Record<string, string>; readonly $listeners: Record<string, Function | Function[]>; }
只要正常的在依赖中安装了vue-router vuex就已经通过模块补充的方式将类型添加到了vue实例上。
在一些项目中,vue-router vuex这些依赖不是通过安装在依赖中引入的,而是通过index.html引入的cdn资源文件,这样在开发过程中我们就无法获取其类型。
这个时候我们可以通过安装@types依赖的方式将类型系统补充到项目中,如npm install @types/jquery --save-dev。
不幸的是vue-router和vuex的types包已经废弃了,只能通过手动去github上下载对应版本的vue-router vuex将types文件引入到项目中,你可以像我一样在项目中新建一个types目录,引入需要的类型声明文件。
这样就可以直接在vue实例上访问到$store $router等属性了。
同理当你想要引入其他的组件库上的一些类型文件时,也是这样的方式。
一些需要注意的问题
在vue开发过程中我们会使用this.$refs去访问某一个具体实例的方法,但是这在ts中是访问不到的常见的,比如要想要使用form组件中的validate方法,我们需要给其加上类型断言
this.$refs.form.validate()变为(this.$refs.form as Vue & {validate:Function}).validate()
来告诉编译器this.$refs.form上有validate方法。
因为类型断言前提条件是是当 S 类型是 T 类型的子集,或者 T 类型是 S 类型的子集时,S 能被成功断言成 T,所以需要在类型断言时合并Vue类型。
同时也可以通过vue-property-decorator提供给我们的装饰器一劳永逸的将该refs添加到computed属性上