为什么要学TS:因为JS是动态的,维护成本高。
得先明白什么是静态语言,什么是动态语言:
静态语言(强语言类型):在编译时变量的数据类型即可确定的语言,C++,Java,C#等;
动态语言(弱类型语言):运行时确定数据类型的语言,JavaScript,PHP,Python等;
TS:
以JS为基础而构建的语言,一个JavaScript的超集,可以在任何支持JS的平台执行,TS扩展了JS并添加了类型,但是,TS不能被JS的解析器执行
安装编译TS的工具包:
npm i -g typescript
编译TS代码的包,提供了 tsc 命令,实现了TS转换为JS
简化运行TS的步骤:
使用 ts-node 包,直接在 node.js 中执行TS代码
npm i -g ts-node (ts-node包提供了ts-node命令)
使用方法:ts-node hello.ts
其实还是ts-node命令在内部偷偷的将 TS --> JS,然后再运行JS代码(不生成任何JS文件)
TS新增的东西:
(1)类型 (2)支持ES新特性(3)添加ES不具备的新特性(4)强大的开发工具(5)丰富的配置选项
TS比JS 的优势:
(1)更早的发现错误(静态语言)
(2)程序中的任何位置的代码都有代码提示,随时随地的安全感,增强开发体验
(3)强大的类型系统提升了代码的可维护性,使得重构代码更加容易
(4)支持最新的ECMAScript语法,优先体验最新的语法
(5)TS 类型推断机制,不需要在代码中每个地方都显示标注类型
TypeScript 常用类型:
(1)JS已有类型
① 原始类型:string / number / boolean / undefined / null / symbol
② 对象类型:object(包括数组、对象、函数等)
(2)TS新增类型:
联和类型,自定义类型(类型别名)、接口、元祖、字面量类型、枚举、void、any等。
原始类型:简单,这些类型,完全按照JS类型名称来书写
let age: number = 18
let myName: string = '123'
let isLoading: boolean = false
对象类型:在 TS 中更加细化,每个具体的对象都有自己的类型语法
数组类型:两种写法(推荐使用number[ ]写法)
// 第一种
let num: number[] = [1,3,5]
// 第二种
let num1:Array<string> = ['a', 'b']
需求,数组中既有number类型,又有string类型,如何写:
let arr: (number | string)[] = [1, 'a', 2, 'b']
类型别名(自定义类型):为任意类型起别名
使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用
type CustomArray = (number | string)[]
let arr1: CustomArray = [1, 'x', 2, 'y']
其中:
(1)使用type关键字来创建类型别名
(2)类型别名(比如此处的CustomArray),可以是任意合法的变量名称
(3)创建类型别名后,直接使用该类型别名作为变量的类型注解即可
函数类型:
1、单独指定参数、返回值类型
2、同时指定参数、返回值类型
(1)单独指定参数、返回值类型
function sum(num1: number, num2: number): number {
return num1 + num2
}
// 第二种写法(不推荐)
const add = (num1: number, num2: number) => {
return num1 + num2
}
(2)同时指定参数和返回值类型(只适用于函数表达式)
当函数作为表达式时,可以通过类似箭头函数的形式的语法来为函数添加类型
const add:(num1:number, num2: number) => number = (num1, num2) => {
return num1 + num2
}
(3)如果函数没有返回值,那么函数的返回值类型为void
// 没有返回值为void
function greet(name: string): void {
console.log('name')
}
(4)可选参数(参数可传可不传)
在可传可不传的参数名称后面添加?(问号)
可选参数只能出现在参数类标的最后,也就是说可选参数的后面不能再出现必选参数
function mySlice (start?: number, end?: number): void {
console.log('起始索引:', start, '结束索引:', end)
}
TS 的对象类型:
JS 的对象是由属性和方法构成的
而 TS 中对象的类型就是在描述对象的结构(有什么类型的属性和方法)
let person: {name: string; age: number; sayHi():void} = {
name: 'zhangsan',
age: 18,
sayHi(){}
}
① 直接使用了{ } 来描述对象结构。属性采用了 属性名:类型的形式;方法采用了 方法名():返回值类型的形式
② 如果方法有参数,就在方法名后面的小括号中指定参数类型(比如greet(name:string): void { })
③ 在一行代码中指定一个属性类型,使用;(分号)隔离
a. 如果一行代码只指定一个属性类型(通过换行来分割多个属性类型),可以去掉分号
b. 方法的类型也可以使用箭头函数形式(比如{ sayHi: ()} => void)
对象的属性或方法,也可以是可选的,这个时候就用到可选属性了
比如,在使用axios({})时,如果发送GET请求,method属性就可以省略
function myAxios(config: {url: string, method?: string}) {
console.log(config)
}
可选属性的语法与函数的可选参数的语法一直,都使用?(问号)来表示
接口:
当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的
(1)使用interface关键字来声明接口
(2)接口名称,可以是任意合法的变量名称
(3)声明接口后,直接使用接口名称作为变量的类型
(4)因为每一行只有一个类型属性,因此,属性类型后面没有;(分号)
// 接口
interface IPerson {
name: string
age: number
sayHi(): void
}
// 对象
let person: IPerson = {
name: 'zhangsan',
age: 20,
sayHi(){}
}
接口和类型别名的对比:
相同点:都可以给对象指定类型
不同点:接口只能为对象指定类型,类型别名不仅可以为对象指定类型,实际上可以为任意类型指定别名
// 接口
interface IPerson {
name: string
age: number
sayHi(): void
}
// 类型别名为对象指定类型
type IPerson = {
name: string
age: number
sayHi(): void
}
// 类型别名为简单数据类型指定别名
type number = number | string
接口继承:
如果有两个接口之间有相同的属性或方法,可以将公共的方法抽离出来,通过继承来实现复用
// 正常
interface point2D {x: number; y: number}
interface point3D {x: number; y: number; z: number}
// 更好的方法
interface point2D {x: number; y: number}
interface point3D extends point2D {z: number}
解释:
(1)使用extends(继承)关键字实现了接口 point3D 的继承了 point2D
(2)继承后,point3D 就有了 point2D 的所有属性和方法