目录
第一章 前言
1.1 介绍TypeScript
-
TypeScript(简称:TS)是 JavaScript 的超集(JS 有的 TS 都有)。
-
从编程语言的动静来区分,TypeScript 属于静态类型的编程语言,JS 属于动态类型的编程语言。 静态类型:编译期做类型检查; 动态类型:执行期做类型检查。 代码编译和代码执行的顺序:先编译再执行。
-
由于TS,在编译时就开始做类型检查了,所以TS 可以提前到在编写代码的同时就发现代码中的错误,减少找 Bug、改 Bug 时间
1.2 TypeScript相比Js的优势
- 更早(写代码的同时)发现错误,减少找 Bug、改 Bug 时间,提升开发效率。
- 程序中任何位置的代码都有代码提示,随时随地的安全感,增强了开发体验。
- 强大的类型系统提升了代码的可维护性,使得重构代码更加容易。
- 支持最新的 ECMAScript 语法,优先体验最新的语法,让你走在前端技术的最前沿。
- TS 类型推断机制,不需要在代码中的每个地方都显示标注类型,让你在享受优势的同时,尽量降低了成本。
1.3 使用TypeScript的准备工作
- 安装ts 的工具包,原因:(浏览器与node只认识js代码,不认识ts代码,安装ts的工具包目的是将ts转为js)
npm i -g typescript
利用tsc -v查看是否安装成功
tsc -v
使用顺序 :
- 例如:创建 hello.ts 文件(注意:TS 文件的后缀名为 .ts)
- 将 TS 编译为 JS:在终端中输入命令,tsc hello.ts(此时,在同级目录中会出现一个同名的 JS 文件)
- 执行 JS 代码:在终端中输入命令,node hello.js
-
使用 ts-node 包,直接在 Node.js 中执行 ts 代码
npm i -g ts-node
利用ts-node -v查看是否安装成功
ts-node -v
使用顺序:直接ts-node hello.ts
第二章 TypeScript的数据类型
2.1 TypeScript的常用类型
2.1.1 概述
- 可以将 TS 中的常用基础类型细分为两类:JS 已有类型 与 TS 新增类型。
- JS已有数据类型:基本数据类型与引用数据类型
- 基本数据类型:number、string、boolean、null、undefined、symbol
- 引用数据类型:object(包括:数据array、函数function、对象等)
- TS新增类型
- 自定义类型(类型别名)、联合类型、接口、元组、自变量类型、枚举、void、any等等
2.1.2 TS使用JS基本数据类型
2.1.2.1 number
let number: number = 23
2.1.2.2 string
let myName: string = '关注ve'
2.1.3.3 boolean
let isYouth: boolean = true
2.1.2.4 null
let a: null = null
2.1.2.5 undefined
let b: undefined = undefined
2.1.2.6 symbol
let s: symbol = Symbol()
2.1.2.7 总结
- 写法: 变量 :数据类型 = 类型值
- 通过数据类型为变量进行约束,从而只能给变量赋值该类型的值
- 代码中查看数据的数据类型:(鼠标悬浮在查看的变量上)
- 当赋值其他类型是报错,例如
2.1.3 TS使用JS引用数据类型
2.1.3.1 数组(array)
- 写法一:变量:数据类型[] = ['该数据类型的值1','该数据类型值2'……]——(推荐)
const numbers: number[] = [1,2,4,5] // 该写法是一个纯数字数组数据类型,数组内只包含数字类型
const strs: string[] = ['1','2','3'] // 数组内只包含字符串类型
- 写法二:变量:Array<数据类型> = ['该数据类型的值1','该数据类型值2'……]
const numbers2: Array<number> = [1,2,4,5]
const strs2: Array<string> = ['1','2','3']
- 解释:针对于以上两种方法,只能定义一个数组,然后数组的值只能是纯规定数据类型的值,数组中不能又其他类型的值
- 需求:如果一个数组中能有nuber、 string、boolean等多个数据类型在同一个数组中
- 解决:联合数据类型: 变量: (数据类型)[] = [字面量,……](后续会对该类型具体解释)
const arr: (number | string | null | boolean)[] = [1,'a',false, null]
// 该写法将|理解成或者,表示arr变量是一个数组,数组里面的值可以有数据类型、字符串类型、null、boolean
2.1.3.2 函数(function)
- 说到函数,我们可以知道有参数和返回值两种
- 写法一:单独指定参数、返回值的类型
- 普通函数:function 函数名 (参数1: 参数类型, 参数2: 参数类型…): 函数返回数据类型 {}
- 箭头函数:const 函数名 = (参数1: 参数类型, 参数2: 参数类型…): 函数返回数据类型 => {}
function add (number1:number, number2: number): number { // 普通函数
return number1 + number2
}
console.log(add(1,2))
const addFn = (number1:number, number2: string): string => { // 箭头函数
return number1 + number2
}
console.log(addFn(2,'fv'))
- 写法二:同时指定参数、返回值的类型(这种方法适用于箭头函数)
-
const 函数名: (参数1: 参数类型,参数2: 参数类型…) => 函数返回数据类型 = (参数1,参数2) => {}
// 只适用于函数表达式,例子:
const addFn: (number1:number, number2: string) => string = (number1,number2) => {
return number1 + number2
}
-
如果函数没有返回值,那么,函数返回值类型为:void
function returnvoid(number:number): void {
console.log("函数返回空值", number)
}
returnvoid(2222)
-
使用函数实现某个功能时,参数可以传也可以不传。这种情况下,在给函数参数指定类型时,就用到可选参数了。
-
可选参数: 在可传可不传的参数名称后面添加 ?(问号)
-
注意:必填参数必须在可选参数之前
function mySlice(start:number,end?:number):void {
console.log("开始:" + start + '结束' + end )
}
mySlice(1)
mySlice(1,6)
2.1.3.3 对象(object)
- JS中的对象是由属性和方法构成的,而TS 中对象的类型就是在描述对象的结构(有什么类型的属性和方法)。
- 直接使用{}来描述对象结构。属性采用属性名: 类型的形式,方法采用方法名(): 返回值类型的形式
- 如果方法有参数,就在方法名后面的小括号中指定参数类型(比如: greet(name: string): void )
- 在一行代码中指定对象的多个属性类型时,使用; / ,( 分号或者逗号) 来分隔
- 如果一行代码只指定一个属性类型( 通过换行来分隔多个属性类型),可以去掉;(分号)。
- 方法的类型也可以使用箭头函数形式(比如 :{sayHi: () => void1}
// 写法例子
let person: {
name: string,
age: number,
say:() => void,
greet(name:string):void
} = {
name: '李四',
age: 18,
// 注意:我们这里最好是跟上面描述的写法写,便于阅读,当然这么写也不会错
// say(){
// console.log("说hello")
// },
// greet:(name) => {
// console.log("和" + name + "说hello")
// }
say:() => {
console.log("说hello")
},
greet(name) {
console.log("和" + name + "说hello")
}
}
person.say()
person.greet('zs')
console.log(person.name,person.age)
// 写在一行
// let person: {name: string, age: number, say(name:string):void} = {
// name: '李四',
// age: 18,
// say(name) {
// console.log("和" + name + "说hello")
// }
// }
2.1.4 联合数据类型
-
联合数据类型:由两个或者多个其他类型组成的类型,表示可以是这些类型中的任意一种
-
利用 | 连接多个数据类型,| 可理解或者
-
写法:变量: (数据类型1,数据类型2…)[] = [字面量,……]
// 该写法将|理解成或者,表示arr变量是一个数组,数组里面的值可以有数据类型、字符串类型、null、boolean
const arr: (number | string | null | boolean)[] = [1,'a',false, null]
//该写法表示data可以是number类型、纯字符串数组、存在null/boolean的数组
const data: number | string[] | (null | boolean)[] = ['111']
2.1.5 类型别名(type)
-
类型别名( 自定义类型 ) : 为任意类型起别名
-
使用场景: 当同一类型(复杂 )被多次使用时,可以通过类型别名,简化该类型的使用。
-
使用方法:
-
使用 type 关键字来创建类型别名
-
类型别名(比如:a,b…但是最好语义化一些),可以是任意合法的变量名称
-
创建类型别名后,直接使用该类型别名作为变量的类型注解即可
type CustomArray = (number | string)[]
const array1: CustomArray = [1,2,'a','v']
const array2: CustomArray = [2,'a','b']
type a = (number | string)[] | number
const array3: a = [1,2,'a','v']
const array4: a = [2,'a','b']
const number: a = 20
2.1.6 接口(interface)
-
当一个对象类型被多次使用时,一般会使用接口 (interface )来描述对象的类型,达到复用的目的
-
使用方法:
-
使用 interface 关键字来声明接口。
-
接口名称(比如:a,b…但是最好语义化一些 ),可以是任意合法的变量名称
-
声明接口后,直接使用接口名称作为变量的类型
// 使用接口 -> 这里是两个对象使用的相同键值对以及类型
interface IPerson {
name: string
age: number,
say:() => void,
greet(name:string):void
}
let person1: IPerson = {
name: '张三',
age: 18,
say(){
console.log("说hello")
},
greet:(name) => {
console.log("和" + name + "说hello")
}
}
let person2: IPerson = {
name: '李四',
age: 18,
say(){
console.log("说hello")
},
greet:(name) => {
console.log("和" + name + "说hello")
}
}
// 多个对象有共同的键值对
// let person1: {
// name: string
// age: number,
// say:() => void,
// greet(name:string):void
// } = {
// name: '李四',
// age: 18,
// say(){
// console.log("说hello")
// },
// greet:(name) => {
// console.log("和" + name + "说hello")
// }
// }
// let person2: {
// name: string
// age: number,
// sex: string,
// say:() => void,
// greet(name:string):void
// } = {
// name: '李四',
// age: 18,
// sex: '男',
// say(){
// console.log("说hello")
// },
// greet:(name) => {
// console.log("和" + name + "说hello")
// }
// }
2.1.6.1 类型别名与接口的区别
-
相同点:都可以给对象指定类型
-
不同点:
-
接口(interface)只能给对象指定类型,而类型别名(type)不仅可以给对象指定类型也可以给为任意类型指定别名
-
写法上接口(interface)类似于声明函数,函数中是不同属性/方法的类型描述,而类型别名(type)类似于赋值类型
interface IPerson {
name: string
age: number,
say:() => void,
greet(name:string):void
}
type IPerson = {
name: string
age: number,
say:() => void,
greet(name:string):void
}
type newarr = number | string
2.1.6.2 接口继承(extends)
-
两个接口之间有相同的属性或方法,可以 将公共的属性或方法抽离出来,通过继承来实现复用
interface Point2D {
x: number
y: number
}
// interface Point3D { x: number; y: number; z: number } // 其中x, y是与point2D的x, y相同
// 使用 继承 实现复用:
interface Point3D extends Point2D {
z: number
}
let p3: Point3D = {
x: 1,
y: 1,
z: 0
}
2.1.7 元组(tuple)
- 场景:在一些特殊情况下,例如地图坐标:使用经纬度坐标来标记位置信息。
- 1、如果我们使用如下方法定义一个数组:
let position: number[]
我们从之前的方法可以知道,这其实定义的是一个纯数字类型的数组,长度任意长
let position: number[] = [39, 114, 1, ,3, 4]
但是,我们实际需要得到的是坐标信息,只需要有经纬度的值就可以了,如果使用以上方法,后面的值就没有意义了
- 2、使用元组类型
let position: [number, string] = [39, '114']
-- 元组:确切的控制了这个数据有多少个元素,以及元素的类型;
-- 该例子是定义了一个变量有两个元素,第一个元素是number数据类型,第二个是string类型;
-- 如果赋值多了会报错,不对应赋值也会报错,如下所示
2.1.8 类型推论
- ts中在某些没有明确指出类型的地方,ts的类型推论机制会帮助提供类型
- 常见场景:1.声明变量并初始化时,2.决定函数返回值时
- 但是如果声明变量没有立即初始化,则必须添加类型注解
否则:
2.1.9 类型断言(as)
- 指定更具体的类型
- as关键字
- 关键字as后面是一个更加具体的类型
const aLink = document.getElementById('link') as HTMLAnchorElement
- 使用<>语法,这种语法形式不常用:(与react语法冲突,不常用)
const aLink = <HTMLAnchorElement>document.getElementById('link')
- 获取具体元素的方法:在浏览器控制台,先选中元素,此时元素后面会被添加$0,通过 console.dir() 打印该 DOM 元素,在属性列表的最后面,即可看到该元素的类型。
2.1.10 字面量类型(const)
- 初体验,代码如下:
let str1 = 'hello'
const str2 = 'hello'
- 根据类型推论我们可以发现,str1与str2的类型分别是string与 'hello'
-
str1 是一个变量(let),它的值可以是任意字符串;而str2 是一个常量(const),它的值不能变化只能是 'hello'
-
除字符串外,任意的 JS 字面量(比如,对象、数字等)都可以作为类型使用
- 使用场景:表示一组明确的可选值列表;一般搭配联合类型一起使用,例如:
2.1.11 枚举(enum)
- 类似字面量类型+联合类型组合的功能,用来表示一组明确的可选值
- 初体验,代码如下:
enum Direction { Up,Down,Right,Left }
function changeDirection(direction: Direction) {
return direction
}
console.log(changeDirection(Direction.Up))
-- 输出结果:
-- 扩展1:
enum Direction { Up=6,Down,Right,Left }
-- 总结:
- 使用enum关键字定义枚举
- 约定枚举名称、枚举中的值以大写字母开头
- 枚举中的多个值之间通过 ,(逗号)分隔
- 定义好枚举后,形参直接使用枚举名称作为类型注解
- 实参的值可以是枚举 Direction 成员的任意一个,接通过点(.)语法访问枚举的成员
- 从输出结果为0,我们可以知道表示枚举的值是会携带默认值的,从 0 开始自增的数值
- 通过扩展1能知道数字的起始值可以自定义,并且自增只针对数字类型
-
枚举中的成员也可以自定义初始化值
-
字符串枚举 :枚举成员的值是字符串
enum Direction { Up = 'up', Down = 'down', Right = 'right', Left = 'left'}
function changeDirection(direction: Direction) {
return direction
}
console.log(changeDirection(Direction.Up))
- 注意: 字符串枚举没有自增长行为,因此,字符串枚举的每个成员必须有初始值。
- 转换为js文件
- 由于其他类型仅仅被当做类型,而枚举不仅用作类型,还提供值(枚举不仅作为类型,还提供值);所有其他类型会在编译成js是自动移除,但是枚举会被编译成js代码;
-
一般情况下, 推荐使用字面量类型+联合类型组合的方式 ,相比枚举更加直观、简洁、高效。
2.1.12 any类型
-
不推荐使用,当值为any类型时,可以对改值进行任意操作,并且不会有代码提示;也不会有报错,失去了ts的意义
-
其他隐式具有any类型情况:1 声明变量不提供类型也不提供默认值 2 函数参数不加类型
2.1.13 typeof
- 初使用:
let number1: number = 10
let number2 = typeof number1
console.log(number2)
let obj1 = {x: 1, y: 2}
let obj2 = typeof(obj1)
console.log(obj2)
function dot(point: { x: number, y: number }) {
return point
}
console.log(dot(obj1))
function dot2(point: typeof obj1) {
return point
}
console.log(dot2(obj1))
- 使用场景:根据已有变量的值,获取该值的类型,来简化类型书写
- 如果直接使用typeof,获取的就是获取变量的类型,如上前两个输出
- typeof 出现在类型注解的位置(参数名称的冒号后面)所处的环境就在类型上下文(区别于 JS 代码),如上最后一个输出。