vue3+Ts 指南
一、Ts的基本类型注解
String类型标注
let name:string='Vue3'
Number类型标注
let test1:number=10 //十进制
let test2:number=0b1010 //二进制
let test3:number=0o12 //八进制
let test4:number=0xa //十六进制
Boolean类型标注
let isShow:boolean=false
Array类型标注
let list1:number[]=[1,2,3]
let list2:Array<string>=['1', '2', '3']
let list3:[string,number] //元组类型
list3=['hello',1] //正确
list3=[1,'hello'] //错误
Object类型标注
- object类型中,对象内部定义的值是不受类型约束的
let obj : object = {
name : '小明',
age : 18
}
function getObj (obj:object) : object {
console.log(obj);
return {
name : ‘张’ + obj.name,
age : obj.name + 30
}
}
null和undefined类型标注
- null和undefined,默认情况下它们是所有类型的子类型,可以赋值给任意类型
- 严格模式下 null和undefined 除自身和void之外不能赋值给其他类型
let demo1:undefined=undefined
let demo2:null=null
let demo3:string | null = 'demo3'
demo3 = null
any类型
let any1:any=4
any1=='哈哈哈
any1=false
let anyList:any[]=[1,true,'free']
void类型
- void类型与any类型相反,它表示没有任何类型
- 一般用来说明函数的返回值不能是undefined和null之外的值
function fn():void{
consoloe.log('void')
//return undefined
//return null
}
接口
- 接口是对象的状态(属性)和行为(方法)的抽象(描述)
interface IPerson{
readonly id:number, // 只读属性
name:string,
age:number,
sex?:string //可选属性
}
type
- type作用就是给类型起一个新名字,支持基本类型、联合类型、元祖或者自定义的类型
type customNumber = number; //基本类型
let num: customNumber = 10;
type userOjb = {name:string} // 对象
let user1: userObj = {name: '张小明'}
type data = [number,string] // 元组
let list: data = [1, '测试']
// 联合类型
interface Obj1 {
name: string;
}
interface Obj2 {
age: number
}
type AllObj = Obj1 | Obj2 // 联合类型
// type AllObj = Obj1 & Obj2 // 交叉类型
// type ObjList = [Obj1, Obj2] // 元组类型
let instanceObj: AllObj = { name: "aa", age: 1 };
// type与接口的差别
// 区别1: 定义类型范围不同
// interface 只能定义对象类型或接口当名字的函数类型
// type 可以定义任何类型,包括基础类型、联合类型、交叉类型、元组
// 区别2: 合并声明
// 定义两个相同名称的接口会合并声明
// 定义两个同名的type会出现编译错误
// 区别3: 拓展性
// interface接口可以使用extends和implements进行拓展
泛型
- 在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型
// 函数中使用泛型
function test <T> (arg:T):T{
console.log(arg);
return arg;
}
test<number>(111);// 返回值是number类型的 111
test<string | boolean>('hahaha')//返回值是string类型的 hahaha
test<string | boolean>(true);//返回值是布尔类型的 true
//接口泛型
interface ibaseCRUD<T>{
data:T[]
add:(t:T)=>void
getById:(id:number)=>T
}
// 泛型约束
interface Lengthwise{
length:number
}
function test<T extends Lengthwise>(x:T):number{
console.log(x.length)
return x.length
}
test('123') //正确
test([1, 2, 3]) //正确
test(123) //错误
//泛型工具类型
// 1. Partial
// partial<T>的作用就是将某个类型中的属性全部变为可选项?
interface Person {
name: string;
age: number;
}
let personqwe: Partial<Person> = { name: "11" };
// 2. Record
// Record<K, T>是将K中所有的属性转换为T类型
type keys = 'A' | 'B' | 'C'
const result: Record<keys, number> = {
A: 1,
B: 2,
C: 3
}
// 3. Pick
// Pick<T, K>的作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型
interface Todo {
title:string,
desc:string,
time:string
}
type TodoPreview = Pick<Todo, 'title'|'time'>;
const todo: TodoPreview ={
title:'吃饭',
time:'明天'
}
联合类型
let value: string | number;
value = 1
value = "a"
// 接口联合类型
interface Obj1 {
name: string;
}
interface Obj2 {
age: number
}
type AllObj = Obj1 | Obj2 // 联合类型
let instanceObj: AllObj = { name: "aa", age: 1 };
交叉类型
interface People {
age: number;
height: number;
}
interface Man {
sex: string;
}
const user: People & Man = { age: 12, height: 170, sex: "男" };
二、Vue3中使用Ts
ref标注类型
<script lang="ts" setup>
import { ref } from 'vue'
import type { Ref } from 'vue'
// 泛型参数类型标注
const age = ref<number>(20); 或者 const age = ref<number | string>(20);
// 接口标注类型
interface book {
name: string
price: number
}
const book = ref<book>({
name: '张小明',
age: 48
})
// 直接给声明的变量添加类型
const name: Ref<string> = ref('张三');
// 为什么不是 const name: string = ref('张三');
// Vue3代理对象用的是Proxy ,Proxy的代理目标必须是引用类型, 所以增加了Ref的类型描述
const sex = ref('男') as Ref<string>;
// 推荐使用前两种方式,前两种方式都是以泛型的形式来标注类型的
// 拓展类型Ref还需要额外的引入
</script>
reactive 标注类型
<script lang="ts" setup>
import { reactive } from 'vue'
interface person {
name: string;
age: number;
sex: string;
}
// 直接给声明的变量添加标注类型
const father: person = reactive({
name: '张三',
age: 30,
sex: '男'
})
// 通过泛型参数添加标注类型
const son = reactive<person>({
name: '小张',
age: 23,
sex: '男'
})
// 注意:官方并不推荐使用reactive泛型参数,因为泛型的参数类型与深层次ref解包的返回值不同, 推荐直接给声明的变量添加类型
<script>
computed 标注类型
<script lang="ts" setup>
import { ref, computed } from 'vue'
import type { Ref, ComputedRef } from 'vue'
const count = ref<number>(10);
// 或者
// const count: Ref<number> = ref(10);
// 通过泛型参数添加标注类型
const doubleCount = computed<number>((): number => count.value * 2)
// 直接给声明的变量添加类型
const trebleCount : ComputedRef<number> = computed((): number => count.value * 3)
const fourfoldCount = computed((): number => count.value * 4) as ComputedRef<number>
<script>
defineProps
<script lang="ts" setup>
import { defineProps } from 'vue'
// 常规写法
const props1 = defineProps({
name1: String,
name2: {
type: String,
required: true
},
list: {
type: Array as () => Array<string>,
required: true,
default: () => ([])
}
})
// 通过泛型参数来定义 props 的类型
interface Props2 {
name1?: string;
name2: string;
list: string[]; // 或者Array<string>
}
const props2 = defineProps<Props2>()
// 泛型参数props定义默认值
interface Props3 {
name1?: string;
name2: string;
list: (string | number)[]; // 或者Array<string | number>
}
const props3 = withDefaults(defineProps<Props3>(), {
name1: "name1111",
name2: "name2222",
list: () => ["1", "2", 3],
});
<script>
defineEmits
<script lang="ts" setup>
import { ref, defineEmits } from 'vue'
interface Emits {
(event: "getMsg", name: string): void;
}
let name = ref<string>("张小明");
const emit = defineEmits<Emits>();
const transmit = (): void => {
emit("getMsg", name.value);
};
<script>
defineExpose
- 向外暴露属性
- setup语法糖 默认组件是关闭的, 无法通过组件实例获取组件的属性和方法
// 子组件--child.vue
<script lang="ts" setup>
import { ref, defineExpose } from 'vue'
let name = ref<string>("张小明");
defineExpose({name})
<script>
// 父组件
<template>
<div class="home">
<Child ref="childElInstance" @getMsg="testEmite"></Child>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import Child from "./Child.vue";
// 获取元素或组件实例
const childElInstance = ref<HTMLImageElement | null>(null); // 或者 const childElInstance = ref<any>(null); 不建议
// 获取组件实例
//const childElInstance = ref<InstanceType<typeof About> | null>(null);
onMounted(() => {
console.log(childElInstance?.value?.name);
});
<script>
provide
- provide提供一个值,可以被后代组件注入
- 接受两个参数,第一个参数称为注入名,也就是key,可以是字符串或者Symbol。第二个参数是值,要传递的数据,任意类型的数据
<script lang="ts" setup>
import { provide } from 'vue';
import type { InjectionKey } from 'vue'
// key为string类型
provide('data', '这是暴露出来的数据');
// key为Symbol类型
// Vue提供了一个InjectionKey接口 用于类型标注
// ES6引入了一种新的原始数据类型Symbol,表示独一无二的值, 可以作为对象的属性名, 避免属性名冲突
const data = Symbol() as InjectionKey<string>; // 这里的声明放到公共的文件中
provide(data, '这是暴露出来的数据');
<script>
inject
- inject注入一个由祖先组件或整个应用供给的值
- 有两个参数,第一个参数称为注入名,也就是key, 第二个参数是指定默认值
<script lang="ts" setup>
import { inject } from 'vue'
import { data } from '------' // 这里引入公共文件申明的Symbol类型数据
// key为string类型
const injectName1 = inject<string>('data')
// key为Symbol类型
const injectName2 = inject(data)
<script>