- TypeScript 可以在JavaScript的基础上,对变量的数据类型加以限制。
- tyscript中文文档
- 在线编辑地址
TypeScript的优点/项目使用TS的好处❗
| 优点 | 描述 |
|---|---|
| 静态类型检查 | 通过静态类型检查,可以在编码阶段就发现和修复错误,提高代码的质量。 |
| 提高项目的可维护性 | 通过类型注解和接口,其他开发者可以更容易理解和使用你的代码,可以提高代码的可读性和可维护性。 |
| 降低团队协作成本 | 在团队开发中,TypeScript的类型系统可以帮助所有人都对代码有相同的理解,降低沟通成本 |
| 更好的兼容性 | TypeScript支持最新的ECMAScript特性,并可以将它们编译到旧版本的JavaScript,以便在老版本的浏览器中运行。 |
(一)基本变量的类型限制
0. 基本类型
- TypeScript最基本的数据类型包括:布尔、数字、字符串、null、undefined
1. any:任意类型
当不确定某个变量是什么类型时,可以使⽤any作为这个变量的类型
2. enum:枚举类型
可以把类型限制在指定的场景之内
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
3. never:任何类型的子类型
任何类型的子类型,也就是说 never 可以赋值给任何类型。
const a: number = '' as never;
const b: object = '' as never;
4. void:没有任何类型
void类型像是与any类型相反,它表示没有任何类型,当一个函数没有返回值时,你通常会见到其返回值类型是 void
5. &:交集提取出两个类型都拥有的类型
6. |:类型联合
定义枚举类型,这样可以把类型限制在指定的场景之内
// 定义course1变量的类型为字符串或者数字,赋值为这两个类型都不会报错
let course1 : string|number = '玩转vue 3'
course1 = 1
// course1 = true // 报错
// type⽤来限制变量只能赋值为⼏个字符串的⼀个,score的取值只能是代码中三个值之⼀。
type courseScore = '好' | '⾮常好' | '嘎嘎好'
let score1 :courseScore = '好'
let score2 :courseScore = '⼀般好' // 报错
(二)对象的类型限制(interface)
通过interface接⼝可以定义对象的类型限制
| 标题 | 说明 |
|---|---|
某个类型+[] | 表示语法定义类型为某个类型组成的数组,等同于Array<某个类型> |
? | 表示 设置 为可选属性 |
readonly | 表示 设置为只读属性,如果对其进⾏修改就会报错 |
// 通过interface接⼝可以定义对象的类型限制
interface 对象的类型限制 {
名称: string,
幸运数字: number[],//使⽤number[] 语法定义类型为数字组成的数组
爱好?: string | boolean,// 通过 ?设置 为可选属性
readonly 国籍: string // readonly设置为只读属性,如果对其进⾏修改就会报错。
}
let person: 对象的类型限制 = {
名称: '哈哈',
幸运数字: [0, 1],
爱好: false,
国籍: '中国'
}
// person.国籍='美国'//报错,只读不可修改
(三)函数的类型限制
- 函数的定义,参数和返回值本质上也是变量的概念,都可以进⾏类型的定义
- ⼤致语法:
function 函数名(参数:参数类型):返回值类型{}
1. function的方式定义函数
// 1. function的方式定义函数
function add(x: number, y: number): number {
return x + y;
}
add(1, 2);
2. 使用变量的方式定义函数(可读性差,不推荐)
let add1: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y
}
add1(2, 3)
3. 使⽤type关键字去定义函数的类型(推荐)
type addType = (a: number, b: number) => number
let add2: addType = function (x: number, y: number): number {
return x + y
}
4. 使⽤interface关键字去定义函数的类型(推荐)
interface addInterface {
(a: number, b: number): number
}
let add3: addInterface = function (x: number, y: number): number {
return x + y
}
5. 函数重载的方式
- 如果你的函数本来就⽀持多个类型的参数,下⾯的代码中reverse函数既⽀持数字也⽀持字符串。
- 我们的要求是:如果参数是数字,返回值也要是数字;参数是字符串,返回值也只能是字符串。
所以参数和返回值都⽤ number|string就没法精确地限制这个需求。 - 我们需要使⽤函数重载的⽅式,定义多个函数的输⼊值和返回 值类型,更精确地限制函数的类型。
// 函数重载
function reverse(x: number): number
function reverse(x: string): string
function reverse(x: number | string): number | string | void {
// typeof判断参数的类型
// 如果参数是数字,返回值也要是数字
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
}
// 参数是字符串,返回值也只能是字符串
else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
(四)宿主环境⾥的类型
| 属性 | 说明 |
|---|---|
Window | window的类型 |
HTMLElement | dom元素类型 |
NodeList | 节点列表类型 |
MouseEvent | ⿏标点击事件的类型 |
IArguments | ⿏函数默认入参的类型 |
// window的类型
let w: Window = window
// dom元素类型
let ele: HTMLElement = document.createElement('div')
// 节点列表类型
let allDiv: NodeList = document.querySelectorAll('div')
// 标点击事件的类型
ele.addEventListener('click', function (e: MouseEvent) {
const args: IArguments = arguments
w.alert(1)
console.log(args)
}, false)
(五)泛型
1. 类型变量 <T>
<T>让我们拥有了给函数的参数定义类型变量的能⼒;- 当我们需要返回值的类型和参数⼀致时,可以在函数名之后使⽤
<>定⼀个泛型T; - 你可以理解这个T的意思就是给函数参数定义了⼀个类型变量,会在后⾯使⽤;
相当于【type T = arg的类 t 型】,返回值使⽤T这个类型就完成了这个需求
// 类型变量的参数是T,通过传入不同的参数,控制不同的类型变化
function identity<T>(arg: T): T { return arg }
// 这个T就是string,所以返回值必须得是string
identity<string>('玩转vue 3全家桶')
// 这个T就是number,所以返回值必须得是number
identity<number>(1)
2. keyof: 获取已知类型的属性(key)列表
keyof语法可以获得已知类型的属性(key)列表,可以理解成获取对象的所有key值
interface VueCourse5 {
name: string,
price: number
}
// 使⽤keyof语法获得已知类型VueCourse5的属性列表,相当于 ‘name’|‘price’:
type CourseProps = keyof VueCourse5 // 等效于 "name" | "price"
let k: CourseProps = 'name' // k 只能是name和price选⼀个
// let k1: CourseProps = 'p' // 报错,改成price就是对的
3. extends:类型系统中的条件判断
- 用法:类型三元表达式
(T extends U) ? X : Y - 使⽤extends来实现类型系统中的条件判断
- extends相当于TypeScript世界中的条件语句
// 定义类型函数ExtendsType,接受泛型参数T后,
// 通过判断T是不是布尔值来返回不同的类型字符串
type ExtendsType<T> = (T extends boolean) ? "重学前端" : "玩转Vue 3"
type ExtendsType1 = ExtendsType<boolean> // type ExtendsType1='重学前端'
type ExtendsType2 = ExtendsType<string> // type ExtendsType2='玩转Vue 3'
4. infer :给extends之后的变量设置类型变量
- 用法:
type InferArray<T> = T extends (infer U)[] ? U : never; - infer可以在extends之后的变量设置类型变量,更加细致地控制类型。
- infer语法的限制如下:
- infer只能在条件类型的 extends 子句中使用
- infer得到的类型只能在
true语句中使用, 即X中使用
// 如果T是⼀个函数,并且函数返回类型是P就返回P
// infer P的意思就是泛型T是函数类型() => infer P,并且这个函数类型的返回类型是P
type ReturnType1<T> = T extends ( () => infer P ) ? P : never
type Foo = () => CourseObj
type Foo1 = ReturnType1<Foo>
5. in 关键字:遍历
- in关键字可以理解为TypeScript世界中的遍历
type Courses = '玩转Vue 3' | '重学前端'
type CourseObj = {
[k in Courses]: number // 遍历Courses类型作为key
}
// 上⾯的代码等于下⾯的定义
// type CourseObj = {
// '玩转Vue 3': number;
// '重学前端': number;
// }
6. 泛型的综合运用
// K extends keyof T:限制K的类型必须是T的属性之⼀
// T[K]是值的类型
function getProperty<T, K extends keyof T>(obj: T, name: K): T[K] {
return obj[name]
}
const coursePrice: CourseObj = {
"玩转Vue 3": 129,
"重学前端": 129
}
getProperty(coursePrice, '玩转Vue 3')
getProperty(coursePrice, '不学前端') // 报错
(六)Pick 和 Omit
1. Pick
- 从类型定义的属性中,选取指定一组属性,返回一个新的类型定义。
- 使用
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
- 示例:
interface Person {
name: string;
age: number;
id: number;
sex: 0 | 1;
}
// 如果不需要 age 这个属性,只选取name和id
type Woman = Pick<Person, "name" | "id">;
// 此时 Woman 等效于 Female
interface Female {
name: string;
id: number;
}
2. Omit
Omit与Pick作用相似,只不过Omit是:以一个类型为基础支持剔除某些属性,然后返回一个新类型。- 使用
type Omit<T, K extends string | number | symbol> = {
[P in Exclude<keyof T, K>]: T[P];
};
- 示例
interface User {
id: number;
name: string;
age: number;
sex: 0 | 1;
tel: number;
}
type EditUser = Omit<User, "id">; // 就是在 User 的基础上,去掉 id 属性
(七)实战 & 练习
1. TypeScript在Vuex4中使用TS实战分享
2. 例题
需求:我们该如何定义Interface API,使其能够限制:
- request只能有buy和comment两个请求地址,
- 并且comment请求的参数中message是必传项
// 实战练习
import axios from 'axios' function request(url: string, obj: any) { return axios.post(url, obj) }
interface Api { }
request('/course/buy', { id: 1 })
request('/course/comment', { id: 1, message: '嘎嘎好看' })
request('/course/comment', { id: 1 }) //如果message必传 怎么类型提醒缺少参数
request('/course/404', { id: 1 }) //接⼝不存在 类型需要报错
实现
import axios from 'axios'
interface Api {
'/course/buy': { id: number },
'/course/comment': { id: number, message: string }
}
function request<T extends keyof Api>(url: T, obj: Api[T]) {
return axios.post(url, obj)
}
(八)知识回顾
- 讲一下 泛型、 infer 关键字、
void和never区别。