首页 > 数据库 > 国内数据库 > 一文快速熟悉vue3中如何正确做ts类型标记
vue3已经出来三年多了,其中新增了组合式
api,并且也提供了更好的
TypeScript类型标记支持,之后还推出了
<script setup>语法糖,使我们的开发体验进一步提升。不知道大家在使用
vue3配合
ts开发时,是否能够正确标记对应的类型,以获得更好的类型提升支持。下面
精心整理了一篇使用
vue3 + typesctypt + <script setup>开发时,如何正确进行类型标记,包括最新的
api使用方法,带你快速上手
vue3 + ts开发。
运行时声明是在代码运行时才会确定参数的类型,在编译阶段不会进行类型检测。
js代码解读复制代码<script setup lang="ts"> const props = defineProps({ title: String, size: { type: String, required: true, validate(val: string) { return ['mini', 'default', 'large'].includes(val) } } }) props.title // title?: string | undefine props.size // size: string </script>
这种声明方式基本和 选项式api的声明方式一致。
对于运行时声明,有时候我们需要给一个
props参数声明为一个
自定义接口类型,这时需要借助
vue内部提供的
PropType工具类型进行类型断言:
ts代码解读复制代码<script setup lang="ts"> import type { IUser } from './types.ts' import type { PropType } from 'vue' const props = defineProps({ data: { type: Object as PropType<IUser>, required: true } }) props.data // data: IUser </script>
基于类型的声明在编译保存代码编译阶段就能确定类型,获得更好的类型提示。通过泛型参数来定义
props的类型比较常用,通过这种方式定义的类型看起来也更加直观。
ts代码解读复制代码<script setup lang="ts"> import type { IUser } from './types.ts' const props = defineProps<{ title?: string, size: 'mini' | 'default' | 'large', data: IUser }>() props.title // title?: string | undefine props.size // size: "default" | "mini" | "large" props.data // data: IUser </script>
以上我们通过泛型定义的参数和上面基于运行时声明参数是一样的,但是明显可以看出这样写简洁了许多。
基于类型的声明在给参数赋默认值时,需要借助
withDefaults 编译器宏来实现。
ts代码解读复制代码<script setup lang="ts"> import type { IUser } from './types.ts' const props = withDefaults(defineProps<{ title?: string, size: 'mini' | 'default' | 'large', data: IUser }>(), { title: '标题', size: 'default', data: () => { return {name: '细狗', age: 18} } }) </script>
注意:引用数据类型
data给默认值时需要通过一个构造方法进行返回,并且给默认值后,原来在定义类型时是必传参,也会自动转化为可选参。
有时候我们不确定
props的参数类型,希望父组件通过传入泛型的方式进行类型定义,这时候我们可以使用
<script> 标签上的
generic 属性声明泛型类型参数:
ts代码解读复制代码<script setup lang="ts" generic="T, U extends Object"> const props = defineProps<{ title?: T, data: U }>() props.title // title?: T | undefined props.data // data: U extends Object </script>
这时,我们在父组件中传递参数时,就能够自动推导出参数对应的类型了:
ts代码解读复制代码<template> <Item :data="user" title="人"></Item> <!-- data:IUser,title?: string | undefined --> <Item :data="dog" :title="0"></Item> <!-- data:IDog,title?: number | undefined --> </template> <script setup> import Item from './Item.vue' import { ref } from 'vue' import type { IUser, IDog } from './types.ts' const user = ref<IUser>({name: '细狗', age: 18}) const dog = ref<IDog>({name: '舔狗',color: '白色'}) </script>
ts代码解读复制代码<script lang="ts" setup> const emit = defineEmits(['change', 'confirm']) emit // emit: (event: "change" | "confirm", ...args: any[]) => void </script>
这种声明方式比较方便,但是无法给触发的事件参数定义类型。如有这个需求,我们可以通过对象字面量的方式解决这个问题。
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') </script>
使用这种方式可以方便的定义多个参数类型和事件返回值类型,也是目前来看比较常见的一种方式。
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事件是一样的,都是没有返回值。
ts代码解读复制代码// 默认值类型推导 const count = ref(0); // count: Ref<number> // 和上面一个等价 const count = ref<number>(0); // count: Ref<number> // 不提供默认值时,类型推导会得到一个包含undefined的联合类型 const count = ref<number>(); // count: Ref<number | undefined>
ts代码解读复制代码const count: Ref<number> = ref() // 不能将类型“Ref<number | undefined>”分配给类型“Ref<number>” // ref()方法返回值类型必须和count定义的类型一致才行 const count: Ref<number | undefined> = ref()
ts代码解读复制代码interface IState { name: string } // state得到的类型推断为: {name: string} const state = reactive({ name: '细狗' }) // 显式声明类型,等价于上面的类型推导 const state: IState = reactive({ name: '细狗' })
ts代码解读复制代码const amount = ref(10) // 隐式推导返回值类型,doubleAmount: ComputedRef<number> const doubleAmount = computed(() => amount.value * 2) // 显示定义返回值类型 const doubleAmount = computed<number>(() => amount.value * 2)
ts代码解读复制代码/* 父组件 */ provide('name', 0) /* 子组件 */ // name没有默认值 const myName = inject<string>('name') // name: string | undefined // 给name提供默认值 const myName = inject<string>('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<string> /** 父组件 */ import { name } from './constant' // provide(name, 0) // 类型“number”的参数不能赋给类型“string”的参数。 provide(name, '奥特曼') /** 子组件 */ import { name } from './constant' const myName = inject<string>(name, '哈哈') // name: string console.log(myName) // 奥特曼
当我们在自定义组件上绑定一个
ref值时,如果需要编辑器能够正确的提示组件内暴露的属性,则需要正确设置组件的
ref类型,下面来看一个例子
ts代码解读复制代码<!-- Item.vue --> <script lang="ts" setup> import { ref } from 'vue' const show = ref(false) const summit = () => { console.log('提交') } const reset = () => { console.log('重置') } // 向外暴露属性或方法 defineExpose({ show, summit, reset }) </script>
ts代码解读复制代码<!-- Parent.vue --> <template> <Item ref="Item"></Item> </template> <script setup> import Item from "./Item.vue"; import { ref} from "vue"; const ItemRef = ref(null) // ItemRef: Ref<null>
以上没有给
ref传递泛型,所以使用
ItemRef时将得不到任何提示,因为是一个
null类型,如下图所示
为了得到
Item的类型,我们首先需要通过
TypeScript中的
typeof得到
Item组件的类型,再使用
TypeScript内置的
InstanceType工具类型来获取其实例类型:
ts代码解读复制代码<script setup lang="ts"> import Item from "./Item.vue"; import { ref} from "vue"; const ItemRef = ref<InstanceType<typeof Item> | null>(null) </script>
这样我们就能得到正确的类型提示了!
如何你只希望读取组件的
$ref实例,或者所有组件都共享的属性,不关心其暴露值,也可以使用
vue内部提供的
ComponentPublicInstance类型
ts代码解读复制代码import type { ComponentPublicInstance } from 'vue' const ItemRef = ref<ComponentPublicInstance | null>(null)
以上在定义组件
props类型时,分别提到了运行时声明和基于类型的声明的用法,在使用泛型声明类型时,如果需要给参数默认值,则需要使用
withDefaults编译宏,进行赋值;除此之外,还说明了如何定义通过父组件传递参数类型的组件。接着说明
emits事件也支持运行时类型和基于类型的定义方式,其中还提到了
vue3.3+新增的一种更为简洁的写法;接着我们简单介绍了
ref、reactive、computed的类型定义方式及一些注意事项;之后说明了在使用
provide/inject时,如何保证
提供和注入类型的一致性;最后在使用自定义组件时,通过
InstanceType<typeof 组件>的方式正确设置组件类型,以获得更好的类型提示支持。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70043704/viewspace-3074052/ ,如需转载,请注明出处,否则将追究法律责任。