1.TypeScript介绍
文章目录
- 1.TypeScript介绍
- 2.TypeScript的基本使用
-
- 2.1 TS的常用类型
-
- 2.1.1 TS的常用类型概述
- 2.1.2 类型注解
- 2.1.3.0 TS常用基础类型
-
- 2.1.3.1 常用基础类型概述
- 2.1.3.2 原始类型
- 2.1.3.3 数组类型
- 2.1.3.4 联合类型
- 2.1.3.5 类型别名(自定义类型)
- 2.1.3.6 函数类型
- 2.1.3.7 void类型
- 2.1.3.8 可选参数
- 2.1.3.9 对象类型
- 2.1.3.9 对象类型可选参数
- 2.1.3.10 接口
- 2.1.3.11 interface(接口) 和 type(类型别名)
- 2.1.3.12接口的继承:
- 2.1.3.13 类型别名的继承
- 2.1.3.14 元组
- 2.1.3.15 类型推论(推断)
- 2.1.3.16 类型断言
- 2.1.3.17 字面量类型
- 2.1.3.18 枚举
- 2.1.3.19 any 类型
- 2.1.3.20 TS中的typeof运算符
- 2.2 TS的高级类型
-
- 2.2.1 TS的高级类型概述
- 2.2.2 class类
-
- 1.概述⚽:TypeScript全面支持ES2015中引入的class关键字,并为其添加了类型注解和其他语法(比如,可见性修饰符等)
- 2.关键字🧨: class
- 3.基本使用🎁🎁:
- 4.解释🥽:
- 5.class的构造函数
- 6.class类的实例方法
- 7.class类继承
- 7.4 implements 的基本使用🍟🍟🍟:
- 7.5 类成员的可见性修饰符
- 8.readonly ==只读修饰符==
- 8.2 基本使用🍿🍿🍿:
- 8.3 总结🥫🥫🥫:
- 2.2.3 类型兼容性
- 2.2.4 交叉类型
- 2.2.5 交叉类型(&) 和 接口继承(extends) 的对比:
- 2.2.6 泛型
- 2.2.7 泛型类
- 2.2.8 泛型工具类型
- 2.2.9 索引签名类型
- 2.2.10 映射类型
- 2.2.11 映射类型 keyof
- 2.2.12 索引类型查询
- 2.3 类型声明文件
- 3.在React中使用TypeScript
- 总结:一年又一年,年年有代码,加油兄弟们.
- 总结:一年又一年,年年有代码,加油兄弟们.
1.1TypeScript 是什么
TypeScript(简称:TS)是JavaScript的超集(JS有的TS都有)。
TypeScript= Type + JavaScript(在JS的基础上,为JS添加了类型支持)。
TypeScript是微软开发的开源编程语言,可以在任何运行JavaScript的地方运行。
// TypeScript 代码:有明确的类型,即: number (数值类型)
let age1: number = 18
// JavaScript 代码:无明确的类型
let age2 = 18
1.2TypeScript为什么要为JS添加类型支持
背景: Js的类型系统存在“先天缺陷”,Js代码中绝大部分错误都是类型错误(Uncaught TypeError) 。
问题:增加了找Bug、改Bug的时间,严重影响开发效率。
从编程语言的动静来区分,TypeScript属于静态类型的编程语言,JS属于动态类型的编程语言。
静态类型:编译期做类型检查;
动态类型:执行期做类型检查。
代码编译和代码执行的顺序:1编译 2执行。 对于JS来说:需要等到代码真正去执行的时候才能发现错误(晚)。
对于TS来说:在代码编译的时候(代码执行前)就可以发现错误(早)。并且,配合VSCode等开发工具,TS可以提前到在编写代码的同时就发现代码中的错误,减少找Bug、改Bug时间。
1.3 TS相比JS的优势
-
更早(写代码的同时)发现错误,减少找Bug、改Bug时间,提升开发效率。
-
程序中任何位置的代码都有代码提示,增强开发体验
-
强大的类型系统提升了代码的可维护性,使得重构代码更加容易。
-
支持最新的ECMScript语法,优先体验最新的语法,让你走在前端技术的最前沿、
-
TS类型推断机制,不需要在代码中的每个地方都显示标注类型,让你享受优势的同时,尽量降低成本。
除此之外,Vue 3源码使用TS重写,Angular默认支持TS,React与TS完美配合,
1.4 安装编译 TS 的工具包
1.4.1安装命令
使用node,全局安装 TS 编译工具包
npm i -g typescript
1.4.2参看版本号
tsc -v
问题:为什么要安装编译TS的工具包?
回答:Node.js/浏览器,只认识JS代码,不认识TS代码。需先编译为js代码后,再运行。
typescript包的功能:把ts文件->js文件
1.5 编译并运行TS代码
1.5.1 整体步骤
-
创建hello.ts文件
-
将ts编译为js,在终端输入命令
tsc hello.ts
(此时,在同级目录中会出现一个同名的hello.js文件) -
执行js代码:在终端中输入命令,
node hello.js
第一步:先创建一个简单的hello.ts文件:
let uname:string = '张三'
console.log(uname)
第二、三步骤:在控制台执行依次执行命令:
tsc hello.ts
node hello.js
1.6 简化运行TS的步骤
问题:每次修改代码后,都要重复执行 两个命令,才能运行TS代码,太繁琐
简化方法:使用 ts-node 包,直接在 Node.js中执行ts代码。
安装命令:
npm i -g ts-node
提示:ts-node提供了 ts-node命令
使用方式:ts-node ts文件
ts-node hello.ts
解释:ts-node命令在内部偷偷的将TS->JS,然后在运行JS代码。
注意:不会生成js文件,仅仅是在内部转换为js代码而已。
2.TypeScript的基本使用
2.1 TS的常用类型
2.1.1 TS的常用类型概述
-
TypeScript是 JS 的超集,TS 提供了JS的所有功能,并且额外的增加了:类型系统。
-
所有的JS代码都是TS 代码。
-
JS有类型(比如,number/string等),但是JS不会检查变量的类型是否发生变化。而TS会检查。TypeScript类型系统的主要优势:可以显示标记出代码中的意外行为,从而降低了发生错误的可能性。
如:1. 类型注解 2. 常用基础类型
2.1.2 类型注解
示例代码:
let age: number = 18
age=20
// age=‘张三’ // 报错:不能将类型‘string’分配给类型’number‘。
说明:代码中的 :number 就是类型注解。
作用:为变量添加类型约束。比如:上述代码中,约定变量age的类型为number(数值类型)
解释:约定了什么类型,就只能给变量赋值该类型的值,否则,就会报错。
2.1.3.0 TS常用基础类型
2.1.3.1 常用基础类型概述
可以将TS中的常用基础类型细分为两类:1 JS已有类型;2 TS 新增类型
-
JS已有类型
- 原始类型:number/string/boolean/null/undefined/symbol(es6新增)。
- 对象类型:object(包括,数组,对象,函数等对象)。
-
TS新增类型
- 联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any等。
2.1.3.2 原始类型
1.类型名称:
原始类型:number/string/boolean/null/undefined/symbol。
特点:简单。这些类型,完全按照JS中类型的名称来书写。
2.类型使用:
yuansi.ts文件
let age: number = 18
let myName: string = 'zs'
let isLoading: boolean = false
let a: null = null
let b: undefined = undefined
let s: symbol = Symbol()
2.1.3.3 数组类型
1.对象类型:object(包括,数组,对象,函数等对象)。
特点:对象类型,在TS中更加细化,每个具体的对象都有自己的类型语法。
1.数组类型的两种写法:(推荐使用==number[]==写法)
2.书写测试的arr.ts文件
// 数组类型
let numbers: number[] = [1,2,5]
let strings: Array<string> = ['a','b','c']
2.1.3.4 联合类型
1.作用:多种类型的合集
2.使用场景:不是指定的单一类型时,可以使用 联合类型。
3.书写测试的 lianhe.ts文件
// 联合类型
//需求:数组中既有 number类型,又有string类型,如何写?
//正确写法:
let arr: (number | string)[] = ['so',1,3,4,'app']
// 错误写法:
// let arr: number | string[] = ['so',1,3,4,'app'] // 报错,不能将number赋值给string
// let arr: number | string[] = [1,3,4] // 不报错,但是不符合上面需求
// let arr: number | string[] = ['so','app] // 不报错,但是不符合上面需求
---------------------------------------------------
// 小括号的作用:
// 加小括号:表示可以是 number或者string中任一类型数组。
// 不加小括号: 表示只能是 | (竖线)左边的类型,或者是右边的类型。
解释:| (竖线)在TS中叫做 联合类型(由两个或多个其他类型组成的类型,表示可以这些类型中的任一类型)
注意:这是TS中联合类型的语法,只有一根竖线,不要与JS中的|或(||)混淆了。
2.1.3.5 类型别名(自定义类型)
1.作用:为任意类型起别名。
2.使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用。
3.关键字: type
4.测试ts文件代码
// 1.非类型别名
// let arr: (number | string)[] = [1,'a',5,7,'opp']
// 2.类型别名
// 设置 类型别名
type CustomArray = (number | string)[]
// 使用 类型别名
let arr1: CustomArray = [1,'a',5,7,'opp']
let arr2: CustomArray = ['xyz',1,2,3]
解释:
- 使用 type 关键字来创建类型别名
- 类型别名(比如,此处的CustomArray),可以是任意合法的变量名称。
- 创建类型别名后,直接使用该类型别名作为变量的类型注释即可。
2.1.3.6 函数类型
函数的类型实际上指的是:函数参数和返回值的类型。
1.用法: 为函数指定类型的两种方式:
方式1:单独指定参数、返回值的类型
方式2:同时指定参数、返回值类的型
2.测试 ts 文件
// 方式1:单独指定参数,返回值类型
function add(num1: number, num2: number): number {
return num1 + num2
}
const add2 = (num1: number, num2: number): number => {
return num1 + num2
}
// 方式2:函数表达式的形式,同时指定参数,返回值类型
const add2: (num1: number, num2: number) => number = (num1, num2) => {
return num1 + num2
}
注意:方式2:同时指定参数,返回值的类型,只适用于函数表达式
解释:当函数作为表达式时,可以通过类似箭头函数形式的语法来玩函数添加类型。
2.1.3.7 void类型
概述:如果函数没有返回值,那么,函数的返回值类型为: void 。
测试代码:
// void 类型默认可以不写
function getUser(name: string): void {
console.log('Hello',name)
}
2.1.3.8 可选参数
1.🎅🏼概述:使用函数实现某个功能时,参数可以传也可以不传。
2.💖关键字: ?
3.✨用法:
🤡可选参数:在可选可不传的参数名称后面添加 ? (英文问号)
😈注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数
4.🎃测试代码:
// 需求:做一个类似 数组的slice方法,可以slice()也可以slice(1)还可以slice(1,4)。
// 如下:
function mySlice(start?: number, end?: number):void {
console.log('起始索引: ', start, '结束索引: ', end)
}
2.1.3.9 对象类型
1.🎅🏼概述:JS中的对象是由属性和方法构成的,而TS中对象的类型就是在描述对象结构(有什么类型的属性和方法)
2.🚀 语法: 使用 {} 来描述对象结构,属性采用属性名:类型的形式;方法采用方法名():类型的形式。
3.👏测试代码:
// 1.对象类型的单行写法:
// 提示: 需要用 ; (分号)隔开。
let person: {name: string; age: number; sayHi(): void; hobby(name: string): void} = {
name: 'zs',
age: 18,
sayHi() {},
hobby(name) {}
}
// 2.对象类型的多行写法:
// 提示1: 逗号 可以用,也可不用.
// 提示2: 多行写法,(不用逗号)每行建议只出现一个属性/方法,(用逗号)每行出现n个属性/方法都会不会报错
let person2: {
name: string,
age: number,
sayHi(): void,
hobby(name: string): void
} = {
name: 'zs',
age: 18,
sayHi() {},
hobby(name) {}
}
// 3.对象类型的箭头函数写法:
let person3: {
name: string,
age: number,
sayHi()=> void,
} = {
name: 'zs',
age: 18,
sayHi() {}
}
2.1.3.9 对象类型可选参数
💃 1.概述: 对象的属性或方法,也可以是可选的,此时需用可选属性了。
🎅🏼2.语法: 属性?:类型 ,在可选属性的右边添加问号即可。
🤡3.测试代码:
// 需求:我们在使用 axios({...})时,如果发送GET请求,method属性就可以省略。
// 代码如下:
function myAxios(config: {url: string; method?: string}) {
console.log(config)
}
// 总结: 可选属性的语法 与 函数可选参数的语法一致,都使用 ? (问号) 来表示。
2.1.3.10 接口
1.概述🎶: 当一个对象类型被多次使用时,一般会使用 接口(interface) 来描述对象的类型,达到复用的目的。
2.功能特点✨:
- 使用 interface 关键字来声明接口。
- 接口名称(比如,此处的Person),可以是任意合法的变量名称。
- 声明接口后,直接使用接口名称作为变量的类型。
- 因为每一行只有一个属性类型,因此,属性类型没有==; (分号)==。
3.🎃测试代码:
// 设置 接口
interface Person {
name: string
age: number
sayHi(): void
}
// 使用 接口
const person: Person = {
name: 'zs',
age: 18,
sayHi() {}
}
// 提示: 接口建议首字母大写。
2.1.3.11 interface(接口) 和 type(类型别名)
1.两者的区别一😒:
(1)相同点😊: 都可以给对象指定类型。
(2)不同点😈:
- 接口: 只能为对象指定类型。
- 类型别名: 不仅可以为对象指定类型,实际上可以为任意类型指定别名。
(3)🎃测试:
//1设置 接口
interface Person {
name: string
age: number
sayHi(): void
}
//1使用 接口
const person: Person = {
name: 'zs',
age: 18,
sayHi() {}
}
//2设置类型别名
type Person2 = {
name: string
age: number
sayHi(): void
}
//2使用类型别名
const person2: Person2 = {
name: 'zs',
age: 18,
sayHi() {}
}
//3.类型别名 指定 任意类型
type ResData = (number | string)[]
2.1.3.12接口的继承:
🐱👤概述:(1)如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用。
🎁关键字: extends
😎用法: 新接口 extends 要继承的旧接口
🎅🏼功能:接口实现复用
💕代码测试:
// 比如,这两个接口都有 x,y 两个属性,重复写两次,可以,但是繁琐
interface Point2D {x: number; y: number}
interface Point3D {x: number; y: number; z: number}
// 改进:使用 extends 关键字
interface To2D {x: number; y: number} // 要继承的旧接口
interface To3D extends To2D {z: number;}
总结:
1.使用extends(继承)关键字实现了接口To3D继承To2D.
2.继承后,To3D就有了To2D的所有属性和方法.(此时To3D同时有x,y,z三个属性.)
2.1.3.13 类型别名的继承
🐱👤概述:(1)如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用。
🎁关键字: &
😎用法: 新类型别名 = 旧类型别名 & 新增的类型
🎅🏼功能: 类型别名实现复用
💕代码测试:
// 旧类型别名 (要被继承的类型别名)
type P2D = {x: number; y: number}
// P3D 继承了 P2D 的类型别名,并增加一个 z 属性的类型定义
type P3D = P2D & {z: number}
2.1.3.14 元组
1.🌏场景:在地图中,使用经纬坐标来标记位置信息.
2.💦问题:可以使用数组来记录坐标,那么,该数组中只有两个元素,并且这两个元素都是数值类型.
3.🚗测试代码:
let position: number[] = [39.2324, 116.343234]
使用number[]的缺点:不严谨,因为该类型的数组中可以出现任意多个数字.
4.概述❣: 元组(Tuple)
元组类型是另一种类型的数组,它确切地知道包含多少个元素,以及特定索引对应的类型.
5.元组的作用💥:
-
元组类型可以确切地标记出有多少个元素,以及每个元素的类型.
在明确数组中元素个数,以及索引对应的类型时,需要用到 元组.
6.代码测试💘💘💘:
let position: [number, number] = [39.4545, 48.89322]
2.1.3.15 类型推论(推断)
1.概述🔥:并不是所有的地方都要有类型注释,有两个地方,可以不使用类型注释,系统会自动进行 类型推断.
-
变量命名并初始化(图一)
-
决定函数返回值时(图二)
(图一)
(图二)
2.🌊代码测试:
// 声明变量并立即初始化值,此时,可以省略类型注解,
let age = 18
// age = '' // 报错,正常现象
// 注意: 如果声明变量但没有立即初始化值,此时,还必须手动添加类型注解
let a: number
a = 19
// a = '' // 报错,正常现象
// 错误写法: let a
// a = 15
// a = '' // 不报错,非正常现象
//---------------- 分割线 ------------------
// 当函数参数被类型注解时,如果没有写返回值类型,ts会自动触发 类型推断
function add(num1: number, num2: number) {
return num1 + num2
}
// 注意: 返回值类型 是根据 return 后的参数的类型推断的而来的. 如果去掉 函数体里的内容,那么函数的返回值类型 为默认的 void类型.
注意🚀:这两种情况下,类型注解可以省略不写!
推荐⚡:能省略类型注解的地方就省略(
偷懒,充分利用TS类型推断,提升开发效率)技巧🏍:如果不知道类型,可以通过鼠标放在变量名称上,利用VSCode的提示来查看类型.
2.1.3.16 类型断言
1.🚀概述:在某些时候,当你比TS更明确一个值的类型时,可以使用 类型断言来指定更具体的类型.
2.⚓关键字: as
3.🛸使用:
html代码:
<a href='www.baidu.com' id='link'>点击去百度</a>
ts代码:
const aLink = document.getElementById('link') as HTMLAnchorElement
// 注意: getElementByld方法返回值的类型是HTMLElement,该类型只包含所有标签公共的属性或方法,不包含a标签特有的href等属性。
// 因此,这个类型太宽泛(不具体),无法操作href等 a 标签特有的属性或方法。
// 解决方式:这种情况下就需要使用类型断言指定更加具体的类型。
解释:
- 使用 as 关键字实现类型断言
- 关键字 as 后面的类型是一个更加具体的类型(HTMLAnchorElement 是 HTMLElement的子类型).
- 通过类型断言,aLink的类型变得更加具体,这样就可以访问a标签的属性或方法了.
另一种方法,使用<>语法,这种语法形式不常用,知道即可:
const aLink = <HTMLAnchorElement>document.getElementById('link')
4.注意点🌌:
问题:怎么知道 a标签的具体类型?
答案:(最笨的方法)在浏览器控制台,通过console.dir()打印a的标签,在属性列表的最后面,即可看到该元素的类型.
2.1.3.17 字面量类型
1.概述💦:某个特点的字符串也可以作为TS中的类型.
2.💧思考一下代码的类型推断,两个变量的类型分别是什么?
(1).变量str1的类型为: string
(2).变量str2的类型为: ‘Hello TS’
解释:
1.str1是一个变量 (let) ,它的值可以是 任意字符串,所以类型为: string
2.str2是一个常量 (const),他的值不能变化只能是 ‘Hello TS’,所以,它的类型为: ‘Hello TS’
注意:除字符串之外,任意的JS字母量(比如,对象.数字等)都可以作为字面量类型使用.
3.使用模式🛴:字面量类型配合联合类型一起使用.
4.使用场景🛫: 用来表示一组明确的可选值列表.比如:在贪吃蛇游戏中,游戏的方向的可选值只能是上下左右中的任意一个.
5.代码测试🪐:
function changeDirection(direction: 'up' | 'down' | 'left' | 'right') {
console.log(direction)
}
// 提示: 参数direction的值只能是 up/down/left/right 中的任意一个.
// 优势: 相比于 string 类型,使用字面量类型更加精确,严谨.
2.1.3.18 枚举
1.概述🪂:枚举的功能类似于字面量类型+联合类型组合的功能,也可以表示一组明确的可选值.
2.枚举:定义一组命名常量.它描述一个值,该值可以是这些命名常量中的一个.
3.关键字💥: enum
4.代码测试🚀:
// 定义 枚举
enum Direction { Up, Down, Left, Right }
// 使用 枚举
function changeDirection(direction: Direction) {
console.log(direction)
}
// 怎么使用枚举里的实参?
// 答案: 类似于 JS中的对象,直接通过 点(.)语法 访问枚举的成员.
changeDirection(Direction.Up)// 枚举.属性名
5.注意🔥:
- 使用 enum 关键字定义枚举.
- 约定枚举名称,枚举中的值以大写字母开头.
- 枚举中的多个值之间通过==,(逗号)分割==.
- 定义好枚举后,直接使用枚举名称作为类型注释.
6.给枚举的实参设置值 被称为 数字枚举
问题🐱👓:我们把枚举成员作为了函数的实参,它的值是什么呢?
答案:通过将鼠标移入 Direction.Up,可以看到枚举成员Up的值为0.
注意:枚举成员是有值的,默认为:从0开始自增的数值.
我们把,枚举成员的值为数字的枚举,称为: 数字枚举.
当然,也可以给枚举中的成员初始化值.
测试代码🎃:
// 枚举成员设置 初始值
enum Direction { Up = 10, Down, Left, Right} // 那么 Down为11,Left为12....
enum Direction { Up = 2, Down = 4, Left = 8, Right = 16}
7.字符串枚举
1.概述🎉🎉🎉:枚举成员的值是字符串.
2.注意🎗🎗🎗:字符串枚举没有自增长行为,因此,字符串枚举的每个成员必须有初始值.
3.测试代码🎈🎈🎈:
// 给枚举成员 赋值 字符串
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
8.编译枚举
1.概述🧧:枚举是TS为数不多的非JavaScript类型扩展(不仅仅是类型)的特性之一.
2.原因👓:因为其他类型仅仅被当做类型,而枚举不仅用作类型,还提供值(枚举成员都是有值的).也就是说,其他的类型会在编译为JS代码时自动移除.但是,枚举类型会被编译为js代码!
如下图:
3总结🏆🏆🏆:
枚举与前面讲到的字面量类型+联合类型组合的功能类似,都用来表示一组明确的可选值列表.
一般情况下,推荐使用字面量类型+联合类型组合的方式,因为相比枚举,这种方式更加直观.简洁.高效
2.1.3.19 any 类型
原则👓:不推荐使用any!因为当值的类型为any时,可以对该值进行任意操作,并且不会有代码提示.
解释🏐🏀🥎:以上操作都不会有任何类型错误提示,即使可能存在错误!
尽可能的避免使用any类型,除非临时使用any来避免书写很长,很复杂的类型!
其他隐式具有any类型的情况🎭:
- 声明变量不提供类型也不提供默认值
- 函数参数不加类型