原始类型
let age: number = 18
let myName: string = '老师'
let isLoading: boolean = false
数组类型
// 写法一:
let numbers: number[] = [1, 3, 5]
// 写法二:
let strings: Array<string> = ['a', 'b', 'c']
联合类型
// 通过联合类型将多个类型组合成一个类型
let timer: number | null = null
timer = setInterval(() => {}, 1000)
// 定义一个数组,数组中可以有数字或者字符串, 需要注意 | 的优先级
let arr: (number | string)[] = [1, 'abc', 2]
类型别名 使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用
type CustomArray = (number | string)[]
let arr1: CustomArray = [1, 'a', 3, 'b']
let arr2: CustomArray = ['x', 'y', 6, 7]
函数类型
函数的类型实际上指的是:函数参数和返回值的类型
- 为函数指定类型的两种方式:
- 单独指定参数、返回值的类型
- 同时指定参数、返回值的类型
1. 单独指定参数、返回值的类型:
```ts
// 函数声明
function add(num1: number, num2: number): number {
return num1 + num2
}
// 箭头函数
const add = (num1: number, num2: number): number => {
return num1 + num2
}
2. 同时指定参数、返回值的类型:
type AddFn = (num1: number, num2: number) => number
const add: AddFn = (num1, num2) => {
return num1 + num2
}
void 类型
function greet(name: string): void {
console.log('Hello', name)
}
如果指定 返回值类型为 undefined,此时,函数体中必须显示的 return undefined 才可以
const add = (): undefined => {
// 此处,返回的 undefined 是 JS 中的一个值
return undefined
}
可选参数
// 参数可以传也可以不传
function mySlice(start?: number, end?: number): void {
console.log('起始索引:', start, '结束索引:', end)
}
对象类型
// 空对象
let person: {} = {}
// 有属性的对象
let person: { name: string } = {
name: '同学'
}
// 既有属性又有方法的对象
// 在一行代码中指定对象的多个属性类型时,使用 `;`(分号)来分隔
let person: { name: string; sayHi(): void } = {
name: 'jack',
sayHi() {}
}
// 对象中如果有多个类型,可以换行写:
// 通过换行来分隔多个属性类型,可以去掉 `;`
let person: {
name: string
sayHi(): void
} = {
name: 'jack',
sayHi() {}
}
箭头函数形式的方法类型
type Person = {
greet: (name: string) => void
greet(name: string):void
}
// - 注意:直接使用 `{}` 形式为对象添加类型,会降低代码的可读性(不好辨识类型和值)
let person: {
name: string
sayHi(): void
} = {
name: 'jack',
sayHi() {}
}
// 推荐:**使用类型别名为对象添加类型**
let person: Person = {
greet(name) {
console.log(name)
}
}
接口类型
interface IPerson {
name: string
age: number
sayHi(): void
}
let person: IPerson = {
name: 'jack',
age: 19,
sayHi() {}
}
### interface vs type
- interface(接口)和 type(类型别名)的对比:
- 相同点:都可以给对象指定类型
- 不同点:
- 接口,只能为对象指定类型
- 类型别名,不仅可以为对象指定类型,实际上可以为任意类型指定别名
- 推荐:**能使用 type 就是用 type**
那为什么要用呢?
### 接口继承
如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用
interface Point2D { x: number; y: number }
// 继承 Point2D
interface Point3D extends Point2D {
z: number
}
// 继承后,Point3D 就有了 Point2D 的所有属性和方法(此时,Point3D 同时有 x、y、z 三个属性)
元组类型
元组类型是另一种类型的数组,它确切地知道包含多少个元素,**以及特定索引对应的类型**
let position: [number, number] = [39.5427, 116.2317]
1. 元组类型可以确切地标记出有多少个元素,以及每个元素的类型
2. 该示例中,元素有两个元素,每个元素的类型都是 number
字面量类型
字面量类型配合联合类型一起使用
type Direction = 'up' | 'down' | 'left' | 'right'
function changeDirection(direction: Direction) {
console.log(direction)
}
枚举类型
定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个
// 创建枚举
enum Direction { Up, Down, Left, Right }
// 使用枚举类型
function changeDirection(direction: Direction) {
console.log(direction)
}
类型断言
const aLink = document.getElementById('link') as HTMLAnchorElement
or
const aLink = <HTMLAnchorElement>document.getElementById('link')
// 关键字 as 后面的类型是一个更加具体的类型(HTMLAnchorElement 是 HTMLElement 的子类型)
// 通过类型断言,aLink 的类型变得更加具体,这样就可以访问 a 标签特有的属性或方法了
泛型
//泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活可复用
//调用函数的时候再定义类型,确保参数和返回值类型相同
// 定义函数
function id<Type>(value: Type): Type { return value }
function id<T>(value: T): T { return value }
// 调用函数,尖括号中指定具体的类型
const num = id<number>(10)
const str = id<string>('a')
// 简化泛型函数调用
let num = id(10)
let str = id('a')
// 传入实参 10,TS 会自动推断出变量 num 的类型 number,并作为 Type 的类型
泛型约束
// 泛型函数的类型变量 Type 可以代表多个类型,这导致无法访问任何属性
// 将类型修改为 `Type[]`(Type 类型的数组),因为只要是数组就一定存在 length 属性,因此就可以访问了
function id<Type>(value: Type[]): Type[] {
console.log(value.length)
return value
}
## 添加约束
// 创建一个接口
interface ILength { length: number }
// 表示传入的 类型 必须满足 ILength 接口的要求才行,也就是得有一个 number 类型的 length 属性
function id<Type extends ILength>(value: Type): Type {
console.log(value.length)
return value
}
// 通过 `extends` 关键字使用该接口,为泛型(类型变量)添加约束
// 该约束表示:**传入的类型必须具有 length 属性
多个类型变量
// 泛型的类型变量可以有多个,并且**类型变量之间还可以约束**(比如,第二个类型变量受第一个类型变量约束)
// Type extends object 表示: Type 应该是一个对象类型,如果不是 对象 类型,就会报错
// keyof 关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型
// 类型变量 Key 受 Type 约束,可以理解为:Key 只能是 Type 所有键中的任意一个,或者说只能访问对象中存在的属性
function getProperty<Type extends object, Key extends keyof Type>(obj: Type, key: Key) {
return obj[key]
}
泛型接口
// 定义接口时先使用泛型,等调用接口时再定义类型
interface IdFunc<Type> {
id: (value: Type) => Type
ids: () => Type[]
}
// 定义函数
let obj: IdFunc<number> = {
id(value) { return value },
ids() { return [1, 3, 5] }
}
ts + vue3
defineProps
// defineProps配合ts的泛型定义props类型校验
defineProps<{
money: number
car?: string
}>()
const { money, car = '小黄车' } = defineProps<{
money: number
car?: string
}>()
defineEmits
const emit = defineEmits<{
(e: 'changeMoney', money: number): void
(e: 'changeCar', car: string): void
}>()
ref
// 简单类型不推荐定义
// 复杂类型推荐使用泛型
type Todo = {
id: number
name: string
done: boolean
}
const list = ref<Todo[]>([])
setTimeout(() => {
list.value = [
{ id: 1, name: '吃饭', done: false },
{ id: 2, name: '睡觉', done: true }
]
})
// dom
const imgRef = ref<HTMLImageElement | null>(null)
computed
// 通过泛型可以指定computed计算属性的类型,通常可以省略
const leftCount = computed<number>(() => {
return list.value.filter((item) => item.done).length
})
事件处理
const move = (e: MouseEvent) => {
mouse.value.x = e.pageX
mouse.value.y = e.pageY
}
<h1 @mousemove="move($event)">根组件</h1>
非空断言
// 告诉typescript, 明确的指定obj不可能为空
let nestedProp = obj!.second;