你好,TypeScript
这是我参与「第四届青训营 」笔记创作活动的第2天
由于JS是弱类型语言,缺乏对类型的检验和错误判断,因此在大型复杂项目中常常使用TS去实现相关功能需求,确保代码的安全可靠性,虽然会增加代码量,但所带来的好处会更大,需要知道TS是类型的世界,通过本文可以带你了解ts并学会运用
本地安装ts的方式:npm install -g typescript
使用tsc全局命令:
- tsc -v //查看tsc版本
- tsc fileName.ts //编译ts文件,会生成对应的js文件
- ndoe fileName.js //运行生成的js文件即可查看输出结果
1.原始数据类型
JS的数据类型包括:Boolean,Null,Undefined,Number,BigInt,String,Symbol
在ts中声明这些类型的方法如下
// 变量名: 类型
let isDone: boolean = false
let age: number = 10
let binaryNumber: number = 0b1111
//同样支持es6新增的模板字符串
let firstName: string = 'xiaoming'
let firstName: string = `Hello, ${firstName}, age is ${age}`
//经常被误解的两兄弟,undefined和null是所有类型的子类型。即undefined类型的变量可以赋给number类型变量
let u: undefined = undefined
let n: null = null
let num: number = undefined
TS新增类型
- any:保留了在不确定类型时js的用法
let notSure: any = 4
notSure = 'maybe it is a string'
//也允许调用任何方法
notSure.myName
notSure.getName()
- Array: 使用类型+[]的形式表示数组,数组的方法在传参时也会对类型进行限制
let arrOffNumbers: number[] = [1,2,3,4] //限定了元素只能是number类型
- Tuple:和数组非常类似,区别在于将类型写在了里面,对每一项都起到了限定的作用, 多写和少写参数都会报错
let user: [string, number] = ['xiaoming', 20]
- 枚举Enums:枚举可以用enum关键词来定义,通过定义一系列的方向,在这里面的值,枚举成员会被赋值为从0开始递增的数字; 该枚举变量还可以通过索引进行反向映射,具体如下
enum Direction {
up,
down.
left,
right,
}
console.log(Direction.Up)
//通过枚举变量的索引进行映射
console.log(Direction[0])
字符串枚举
enum Direction {
Up = 'UP',
DOwn = 'DOWN',
Left = 'LEFT',
RIght = 'RIGHT'
}
const value = 'UP'
if(value === Direction.Up) {
console.log('go up!')
}
}
2.interface接口
接口是一系列抽象方法的声明,是方法特征的集合,在js中不存在interface,有了接口,才是真正的面向OOP
//接口定义
interface: Person {
name: string,
age: number
}
//定义一个变量,类型为person。依次约束变量的形状必须和接口保持一致
let xiaoming: person = {
name: 'xiaoming',
age?: 20 //使用'?'表示可选属性
readonly id: number; //使用readonly声明只读属性
}
3.函数
TS的函数最大区别在于约定了输入和输出,语法上有点类似于python的notebook写法,可以直接看到函数的输出类型
// ?可选参数
function add(x: number, y: number, z?: number): number {
if(typeof z === 'number') {
reuturn x+y+z
} else {
return x+y;
}
}
// 赋值的方式直接确定函数本身的类型
const add2 = (x: number, y: number, z?: number) => number = add
// interface描述函数类型
const sum = (x: number, y: number) => {
return x+y;
} interface ISum {
(x: number, y:number) => number
}
const sum2: ISum = sum
4.类型推论,联合类型和类型断言
联合类型-union types
当不确定多种类型中的一种是可以用中竖线进行分割
let numberOrString: number | string
//缺点:只能访问联合类型的所有类型中共有的属性和方法
numberOrString.length
numberOrString .toString
类型断言-type assertions
使用as关键字告诉ts编译器是什么类型,编译器就可以使用该类型的方法
function getLength(input: string | number): number {
const str = input as string
if(str.length) { //先用as告诉编译器类型,从而使用length方法不会报错
return str.lenght
} else {
const number = input as number
return number.toString().length
}
}
类型守卫
TS在不同的分支里面,智能的缩小了范围,减少代码出错的几率
function getLength2(input: string | number): number {
if(typeof input === 'string') {
return input.length
} else {
return ipnut.toString().length
}
}
类型别名和交叉类型
通过类型别名可以给类型起一个别名,更方便的被重用,使用type属性取别名
let sum: (x: number, y: number) => number
const result = sum(1,2)
type PlusType = (x: number, y: number) => number
let sum2: PlusType
// 支持联合类型
type StrOrNumber = string | number
let result2: StrOrNumber = '123'
result2 = 123
// 字符串字面量
type Directions = 'Up' | 'Down' | 'Left' | 'Right'
let toWhere: Directions = 'Up'
交叉类型:通过'&'可以同时使用多种类型赋值形成交叉类型
interface IName {
name: string
}
type IPerson = IName & { age: number }
let person: IPerson = { name: 'hello', age: 1
5.Class类
OOP的三大特点:
- 封装(Encapsulation): 将数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口访问该对象
- 继承(Inheritance): 子类继承父类,子类除了拥有父类的所有特性,还有一些更具体的特性
- 多态(Polymorphism): 由继承而产生了相关的不同的类,对同一个方法可以有不同的响应 类成员的访问修饰符
- public修饰的属性是公有的,可以在任何地方被访问到,默认所有的属性和方法都是public的
- private修饰的属性和方法是私有的,不能在声明他的类的外部访问(报错)
- protected修饰的属性或方法是受到保护的,和private类似,区别在于protected在子类中是允许被访问的
class Animal {
name: string,
constructor(name: string) {
this.name = name
}
run() {
return `${this.name} is running`
}
}
const snake = new Animal('lily')
//继承的特性
class Dog extends Animal {
bark() {
return `${this.name} is barking`
}
}
const xiaobao = new Dog('xiaobao')
console.log(xiaobao.run())
console.log(xiaobao.bark())
//重写构造函数,在子类的构造函数中必须使用super调用父类的方法,否则会出错
class Cat extends Animal {
constructor(name) {
super(name)
console.log(this.name)
}
run() {
return 'Meow, ' + super.run()
}
}
const small_cat = new Cat('mao')
console.log(small_cat.run())
类与接口通过类去实现接口
interface Radio {
switchRadio(trigger: boolean): void;
}
class Car implements Radio {
switchRadio(trigger) {
return 123
}
}
class Cellphone implements Radio {
switchRadio() {
}
}
interface Battery {
checkBatteryStatus(): void;
}
//要实现多个接口,只需要中间逗号隔开
class Cellphone implements Radio, Battery {
switchRadio() {
}
checkBatteryStatus() {
}
}
6. 泛型Generics
泛型是在定义函数,接口或类的时候,不预先指定具体的类型,在使用的时候再指定类型的一种特性
function echo(arg) {
return arg
}
const result = echo(123)
function echo<T>(arg: T): T {
return arg
}
const result = echo(123)
//泛型可以传入多个值
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]]
}
const result = swap(['string', 123])
泛型约束:在函数内部使用泛型变量的时候,由于实现不知道属于哪种类型,不能随意操作它的属性或方法
function echoWithArr<T>(arg: T): T {
console.log(arg.length)
return arg
}
// 上例中,泛型 T 不一定包含属性 length,我们可以给他传入任意类型,当然有些不包括 length 属性,那样就会报错
interface IWithLength {
length: number;
}
function echoWithLength<T extends IWithLength>(arg: T): T {
console.log(arg.length)
return arg
}
echoWithLength('str')
const result3 = echoWithLength({length: 10})
const result4 = echoWithLength([1, 2, 3])
泛型与类和接口
class Queue {
private data = [];
push(item) {
return this.data.push(item)
}
pop() {
return this.data.shift()
}
}
const queue = new Queue()
queue.push(1)
queue.push('str')
console.log(queue.pop().toFixed())
console.log(queue.pop().toFixed())
//在上述代码中存在一个问题,它允许你向队列中添加任何类型的数据,当然,当数据被弹出队列时,也可以是任意类型。在上面的示例中,看起来人们可以向队列中添加string 类型的数据,但是那么在使用的过程中,就会出现我们无法捕捉到的错误,
class Queue<T> {
private data = [];
push(item: T) {
return this.data.push(item)
}
pop(): T {
return this.data.shift()
}
}
const queue = new Queue<number>()
//泛型和 interface
interface KeyPair<T, U> {
key: T;
value: U;
}
let kp1: KeyPair<number, string> = { key: 1, value: "str"}
let kp2: KeyPair<string, number> = { key: "str", value: 123}
7.配置文件
示例 通过一些配置可以将ts转为js过程中更符合项目所需
{
"files": ["test.ts", "test2.d.ts"],
"compileOptions": {
"outer": "./output",
"module": "ESNext",
"target": "ES5",
"declaration": true
}