前言
vue3已经出来三年多了,其中新增了组合式
api,并且也提供了更好的
TypeScript类型标记支持,之后还推出了
语法糖,使我们的开发体验进一步提升。不知道大家在使用
vue3配合
ts开发时,是否能够正确标记对应的类型,以获得更好的类型提升支持。下面
精心整理了一篇使用
vue3 + typesctypt + 开发时,如何正确进行类型标记,包括最新的
api使用方法,带你快速上手
vue3 + ts开发。
一、给组件的props参数定义类型
1.运行时声明
运行时声明是在代码运行时才会确定参数的类型,在编译阶段不会进行类型检测。
1.1 运行时-简单类型声明
js代码解读复制代码
这种声明方式基本和 选项式api的声明方式一致。
1.2 运行时-复杂类型声明
对于运行时声明,有时候我们需要给一个
props参数声明为一个
自定义接口类型,这时需要借助
vue内部提供的
PropType工具类型进行类型断言:
ts代码解读复制代码
2.基于类型的声明
基于类型的声明在编译保存代码编译阶段就能确定类型,获得更好的类型提示。通过泛型参数来定义
props的类型比较常用,通过这种方式定义的类型看起来也更加直观。
ts代码解读复制代码
以上我们通过泛型定义的参数和上面基于运行时声明参数是一样的,但是明显可以看出这样写简洁了许多。
3.withDefaults定义参数默认值
基于类型的声明在给参数赋默认值时,需要借助
withDefaults 编译器宏来实现。
ts代码解读复制代码
注意:引用数据类型
data给默认值时需要通过一个构造方法进行返回,并且给默认值后,原来在定义类型时是必传参,也会自动转化为可选参。
4.父组件传入泛型声明
有时候我们不确定
props的参数类型,希望父组件通过传入泛型的方式进行类型定义,这时候我们可以使用
标签上的
generic 属性声明泛型类型参数:
ts代码解读复制代码
这时,我们在父组件中传递参数时,就能够自动推导出参数对应的类型了:
ts代码解读复制代码
二、给组件定义的emits事件标记类型
1.运行时声明emits
ts代码解读复制代码
这种声明方式比较方便,但是无法给触发的事件参数定义类型。如有这个需求,我们可以通过对象字面量的方式解决这个问题。
ts代码解读复制代码// 基于选项 const emit = defineEmits({ change: (id: number) => { // 返回 `true` 或 `false` // 表明验证通过或失败 if(id !== 1) return false return true }, confirm: (_value: string) => { //value前面加个_,表示方法内可以忽略使用 return true } }) // emit // emit: ((event: "change", id: number) => void) & ((event: "confirm", value: string) => void) emit('change', 2) // 参数不是1,发出警告[Vue warn]: Invalid event arguments: event validation failed for event "change". emit('confirm', 'a')
2.基于类型声明emits
使用这种方式可以方便的定义多个参数类型和事件返回值类型,也是目前来看比较常见的一种方式。
ts代码解读复制代码const emit = defineEmits<{ // 无返回值 (e: 'change', id: number, value: string): void (e: 'confirm', value: string): void }>()
除此之外,
vue3.3+,也新出了一种更简洁的语法,当你不关注事件返回值类型时,可以考虑这这种写法:
ts代码解读复制代码const emit = defineEmits<{ change: [id: number, value: string] confirm: [value: string] }>()
这和前面声明的
emits事件是一样的,都是没有返回值。
三、给ref标记类型
1.使用泛型
ts代码解读复制代码// 默认值类型推导 const count = ref(0); // count: Ref// 和上面一个等价 const count = ref (0); // count: Ref // 不提供默认值时,类型推导会得到一个包含undefined的联合类型 const count = ref (); // count: Ref
2.Ref工具类型
ts代码解读复制代码const count: Ref= ref() // 不能将类型“Ref ”分配给类型“Ref ” // ref()方法返回值类型必须和count定义的类型一致才行 const count: Ref = ref()
3.给reactive标注类型
ts代码解读复制代码interface IState { name: string } // state得到的类型推断为: {name: string} const state = reactive({ name: '细狗' }) // 显式声明类型,等价于上面的类型推导 const state: IState = reactive({ name: '细狗' })
四、给computed标记类型
ts代码解读复制代码const amount = ref(10) // 隐式推导返回值类型,doubleAmount: ComputedRefconst doubleAmount = computed(() => amount.value * 2) // 显示定义返回值类型 const doubleAmount = computed (() => amount.value * 2)
五、给provide / inject 标注类型
ts代码解读复制代码/* 父组件 */ provide('name', 0) /* 子组件 */ // name没有默认值 const myName = inject('name') // name: string | undefined // 给name提供默认值 const myName = inject('name', '哈哈') // name: string console.log(myName) // 0
我们上面再父组件中提供了一个
number类型的值,但是子组件中定义要注入的是
string类型的值,这样子是不会报错的!为了保证正确的类型,我们需要使用
Vue提供了一个
InjectionKey 接口,它是一个继承自
Symbol 的泛型类型,可以用来在提供者和消费者之间同步注入值的类型:
我们先建一个
constant.ts保存项目常量的文件,把
key定义在里面,这样子就可以父组件和子组件同时引入这个
key使用了:
ts代码解读复制代码/** constant.ts */ import type { InjectionKey } from 'vue' export const name = Symbol() as InjectionKey /** 父组件 */ import { name } from './constant' // provide(name, 0) // 类型“number”的参数不能赋给类型“string”的参数。 provide(name, '奥特曼') /** 子组件 */ import { name } from './constant' const myName = inject(name, '哈哈') // name: string console.log(myName) // 奥特曼
六、给组件ref引用标记类型
当我们在自定义组件上绑定一个
ref值时,如果需要编辑器能够正确的提示组件内暴露的属性,则需要正确设置组件的
ref类型,下面来看一个例子
ts代码解读复制代码
ts代码解读复制代码
这样我们就能得到正确的类型提示了!
如何你只希望读取组件的
$ref实例,或者所有组件都共享的属性,不关心其暴露值,也可以使用
vue内部提供的
ComponentPublicInstance类型
ts代码解读复制代码import type { ComponentPublicInstance } from 'vue' const ItemRef = ref(null)
总结
以上在定义组件
props类型时,分别提到了运行时声明和基于类型的声明的用法,在使用泛型声明类型时,如果需要给参数默认值,则需要使用
withDefaults编译宏,进行赋值;除此之外,还说明了如何定义通过父组件传递参数类型的组件。接着说明
emits事件也支持运行时类型和基于类型的定义方式,其中还提到了
vue3.3+新增的一种更为简洁的写法;接着我们简单介绍了
ref、reactive、computed的类型定义方式及一些注意事项;之后说明了在使用
provide/inject时,如何保证
提供和注入类型的一致性;最后在使用自定义组件时,通过
InstanceType的方式正确设置组件类型,以获得更好的类型提示支持。