目录
前言
一、结合写法
1. ref
2. reactive
3. defineProps
4. defineEmits
5. computed
6. 事件处理函数
7. html元素引用
8. 组件实例
二、拓展补充
1. keyof 操作符
2. typeof 操作符
3. InstanceType 工具类型
前言
Vue 3与TypeScript结合使用能够提供类型检查和智能提示,增强了代码的可靠性和可维护性。本文整理了项目开发时用到的结合写法,供学习和参考。
一、结合写法
1. ref
在原有的 ref() 之间加 <> ,并且在 <> 里跟ts类型。
ref()
也会隐式地从它的参数中推导类型。
const msg = ref<string>('hello')
import type { Ref } from 'vue'
const msg2: Ref<number> = ref(100)
type Todo = {
id: number
name: string
done: boolean
}
const list = ref<Todo[]>([])
const list: Ref<Todo[]> = ref([])
2. reactive
在声明变量时,在 : 后跟ts类型。
reactive()
也会隐式地从它的参数中推导类型。
import { reactive } from 'vue'
// 推导得到的类型:{ title: string }
const book = reactive({ title: 'Vue 3 指引' })
要显式地标注一个 reactive
变量的类型,我们可以使用接口
import { reactive } from 'vue'
interface Book {
title: string
year?: number
}
const book: Book = reactive({ title: 'Vue 3 指引' })
不推荐使用 reactive()
的泛型参数,因为处理了深层次 ref 解包的返回值与泛型参数的类型不同。
3. defineProps
在原有的 defineProps() 之间加 <> ,并且在 <> 里跟ts类型。
// 注意这种方式无法设置可选参数的默认值
const props = defineProps<{
name?: string
phoneNumber: number
userInfo: object
tags: string[]
}>()
// 指定默认值
withDefaults(defineProps<{
size?: number
labels?: string[]
}>(), {
size: 3,
labels: () => ['default label']
})
上面写法太笨拙,可以使用 响应式语法糖 解构 + defineProps 就行
注意:目前需要 显式地选择开启 ,因为它还是一个实验性特性。
// vite.config.ts
export default defineConfig({
plugins: [
vue({
reactivityTransform: true,
}),
],
});
const { money, car = "宝马车" } = defineProps<{
money: number
car?: string
}>();
4. defineEmits
在原有的 defineEmits() 之间加 <> ,并且在 <> 里跟ts类型。
const emit = defineEmits(['change'])
const handleChange = () => {
emit('change', 'itcast', '你好')
}
interface Emits {
(e: "update:modelValue", value: string): void;
(e: "change", value: string): void;
}
//定义控件事件
const emit = defineEmits<Emits>();
// 基于类型的声明
const emit = defineEmits<{
(e: 'change', id: number): void;
(e: 'update', value: string): void;
}>()
5. computed
computed()
会自动从其计算函数的返回值上推导出类型:
import { ref, computed } from 'vue'
const count = ref(0)
// 推导得到的类型:ComputedRef<number>
const double = computed(() => count.value * 2)
// => TS Error: Property 'split' does not exist on type 'number'
const result = double.value.split('')
你还可以通过泛型参数显式指定类型:
const double = computed<number>(() => {
// 若返回值不是 number 类型则会报错
})
6. 事件处理函数
<script setup lang="ts">
function handleChange(event) {
// `event` 隐式地标注为 `any` 类型
console.log(event.target.value)
}
</script>
<template>
<input type="text" @change="handleChange" />
</template>
没有类型标注时,这个 event
参数会隐式地标注为 any
类型。这也会在 tsconfig.json
中配置了 "strict": true
或 "noImplicitAny": true
时报出一个 TS 错误。因此,建议显式地为事件处理函数的参数标注类型。此外,你可能需要显式地强制转换 event
上的属性:
function handleChange(event: Event) {
console.log((event.target as HTMLInputElement).value)
}
7. html元素引用
模板引用需要通过一个显式指定的泛型参数和一个初始值 null
来创建:
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const el = ref<HTMLInputElement | null>(null)
onMounted(() => {
el.value?.focus()
})
</script>
<template>
<input ref="el" />
</template>
8. 组件实例
<!-- MyModal.vue -->
<script setup lang="ts">
import { ref } from 'vue'
const isContentShown = ref(false)
const open = () => (isContentShown.value = true)
defineExpose({
open
})
</script>
为了获取 MyModal
的类型,我们首先需要通过 typeof
得到其类型,再使用 TypeScript 内置的 InstanceType
工具类型来获取其实例类型:
<!-- App.vue -->
<script setup lang="ts">
import MyModal from './MyModal.vue'
const modal = ref<InstanceType<typeof MyModal> | null>(null)
const openModal = () => {
modal.value?.open()
}
</script>
二、拓展补充
1. keyof 操作符
通过索引类型查询能够获取给定类型中的属性名类型。索引类型查询的结果是由字符串字面量类型构成的联合类型,该联合类型中的每个字符串字面量类型都表示一个属性名类型。
type Point = { x: number; y: number };
type P = keyof Point; // type P = 'x' | 'y'
如果类型具有字符串或数字索引签名,则 keyof 将返回这些类型:
type Arrayish = { [n: number]: unknown };
type A = keyof Arrayish; // type A = number
type Mapish = { [k: string]: boolean };
type M = keyof Mapish; // type M = string | number
type Todo = {
id: number;
text: string;
done: boolean;
}
const todo: Todo = {
id: 1,
text: "Learn TypeScript keyof",
done: false
}
// extends 关键字约束 K 类型必须为 keyof T 联合类型的子类型。 'id' | 'text' | 'done'
function prop<T extends object, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const id = prop(todo, "id"); // const id: number
const text = prop(todo, "text"); // const text: string
const done = prop(todo, "done"); // const done: boolean
2. typeof 操作符
用于获取一个值的类型信息。typeof操作符有两种主要的使用方式:
const num = 42;
const typeOfNum = typeof num; // 类型为 "number"
const obj = { name: 'Alice', age: 25 };
const typeOfObj = typeof obj; // 类型为 "object"
注意:typeof操作符返回的是一个字符串字面量,表示该值的类型。常见的返回值包括 "number"、"string"、"boolean"、"object"、"function"等。
- typeof类型守卫:用于在条件语句中判断一个变量的类型。
function printLength(value: string | number) {
if (typeof value === 'string') {
console.log(value.length); // 在这个条件块中,value被推断为 "string" 类型
} else {
console.log('Value is not a string.');
}
}
在上述代码中,通过使用typeof进行类型检查,可以在不使用显式类型断言的情况下,根据不同的类型执行不同的代码逻辑。
需要注意的是,typeof操作符的结果在某些情况下可能比较特殊,例如:
- typeof null 的结果是 "object",这是由于历史原因造成的。
- 对于函数类型,typeof会返回 "function"。
3. InstanceType 工具类型
该工具类型能够获取构造函数的返回值类型,即实例类型。这里面的T必须是一个构造函数。
class C {
x = 0;
}
type T0 = InstanceType<typeof C>; // C
type T1 = InstanceType<new () => object>; // object
type T2 = InstanceType<any>; // any
type T3 = InstanceType<never>; // any
type T4 = InstanceType<string>; // 编译错误
type T5 = InstanceType<Function>; // 编译错误