接触一下ts

学习TS

1
2
3
4
5
6
7
8
9
10
首先全或者在对应文件夹下npm install typeescript -g  / -D
tsc init 生成ts配置文件
outDir输出目录
rootDir代码根目录
tsc 打包

安装ts-node可以直接运行ts代码,无序将ts编译成js、
npm install ts-node -D
安装nodemon可以自动检测目录中文件改动后自动重新启动
npm install nodemon -D

1
2
3
4
5
6
7
8
9
10
"scripts": {
"dev": "nodemon --watch src/ -e ts --exec ts-node ./src/index.ts"
},
监听src文件夹下后缀名为ts的文件变化,一有变化就重新执行index.ts文件

parcle打包运行
"scripts": {
"dev": "nodemon --watch src/ -e ts --exec ts-node ./src/index.ts",
"start": "parcel ./index.html"
},
概念
1
2
3
4
5
6
7
8
类是拥有相同属性和方法的集合,有静态特征和动态特征
静态特征:属性,姓名属性
动态特征:方法,吃饭走路

创建类的过程
1:在堆中开辟一块空间
2:调用对应构造函数 new XXX() 匹配构造器constructor
3:把对象赋值给对象变量/吧实例赋值给实例对象/改变this指向

类的源码,底层,原生es5

转换为es5函数

引用属性

数组,函数,类,对象,对象数组,集合类set,map,自定义,集合类

1
2
3
4
5
6
7
8
9
10
constructor中的参数本身需要通过this.xx = xx为属性赋值,括号纵只是参数
但是如果添加了public等修饰符,就不用再赋值,会隐式操作挂载为属性
constructor(name,age){
this.name = name
this.age = age
}
添加public等修饰符后
constructor(public name,public age){
不需要再赋值,隐式挂载
}
函数重载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
不使用重载
function(value: number | Message):Message | Array<Message> | undefined {
if(typeof value === 'number'){
return messages.find(item => item.id === value)
} else {
return messages.filter(item => item.type === value)
}
}
缺点:
联合类型中,方法属性会受到限制,会提取公用属性和方法,交集
TS无法根据参数来推倒最终返回的类型中的数据类型
只可以根据方法定义的类型来展现

使用重载
函数重载规则
函数签名 = 函数名 + 函数参数 + 函数参数类型 + 返回值类型,不包括函数体
函数重载定义
由一个实现签名或者多个实现签名组成
外部调用时,只能调用重载签名,不能调用实现签名
调用重载函数时,会根据传递的参数来判断你调用的是哪一个函数
只有一个函数体,只有实现签名配备了函数体
function getMessage(id:number):number
function getmessage(message:Message):Array<Message>
function getMessage(value:any):number | Array<Message> | undefined {
if(typeof value === 'number'){
return messages.find(item => item.id === value)
} else {
return messages.filter(item => item.type === value)
}
}

有利于功能扩展
任何类型都是unknown的子类型
何时用any,何时用unknown
unknown只能作为父类,不能作为子类
let x:unknown = 3
let z:number = x
方法重载
1
2
3
4
5
规则与函数重载相同,有细小区别
方法是特定情境下的函数,由对象变量/实例变量直接调用的函数都是方法
let p = new P()
p.sendMessage()//方法
方法签名 = 方法名称 + 方法参数 + 方法参数类型 + 方法返回值类型

在new时,会隐式的将this返回给左边的对象变量。this和等号左边变量都指向当前正创建的对象

重载应用,求图形面积
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
type type_s = {
width:number,
height:number,
radious?:number
}
class Square {
public width:number
public height:number
constructor(width:number,height:number) // 重载签名
constructor(type:type_s) // 重载签名
constructor(value:any,height?:number){
this.width = value
this.height = height
if(typeof value === 'object'){
let { width,height } = value
this.width = width
this.height = height
}else{
this.width = value
this.height = height
}
}
public getArea(){
return this.width * this.height
}
}
let s = new Square(30,40)
let s2 = new Square({width:20,height:30})
单件设计模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
单件设计模式概述
一个类对外有且仅有一个实例
如果一个类对外只提供一个对象类,并且在该类的内部提供了一个外部可以访问该对象的属性和方法
通过静态方法访问
public static getXXX(){

}
静态方法和对象无关,外部变量无法访问静态方法和属性,可以通过类名调用
静态方法和原型方法/对象方法是不同的,互相都不能访问
一个静态方法改变了某个静态属性,其他静态方法或者外部任何地方访问这个属性都会改变
除了栈,堆之外,还有静态内存区
静态成员保存在内存的静态区,静态成员的内存分配要早于对象空间的分配,也就是对象创建之前,Ts就已经为静态成员分配好了空间,一个静态方法分配一个空间,只要服务器电脑不重启,静态方法就一直保存在内存空间,无论调用多少次,访问的都是同一片空间
export default class MyLocal{
// 饿汉式 直接先创建
static localStorage:MyLocal = new MyLocal()
private constructor(){

}
// 给外部提供访问内部对象的方法
public static getConstructor(){
// 懒汉式 调用方法才创建
if(!this.localStorage){
this.localStorage = new MyLocal()
}
return this.localStorage
}
public static setItem(key:string,value:any){
localStorage.setItem(key,JSON.stringify(value))
}
public getItem(key:string){
let value = localStorage.getItem(key)
return value != null ? JSON.parse(value) : null
}
public removeItem(key:string){
localStorage.removeItem(key)
}
}


import { MyLocal } from './MyLoacl.js'
MyLocal.getConstructor()

什么情况适合采用单件模式。较多复用的内容,后续多使用,方法类,对象属性并不多,创建实例意义不大
继承Loading学习到5-5先跳过
1

类型断言
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// 类型断言,类型转换
// a数据类型 as b数据类型
// a和b必须要有交叠
// 判断交叠的条件

// 场景1 a和b都是类并且是父子继承关系 通常是将父类类型转换为子类类型
// class Father {
// public name:string
// }
// class Son extends Father{
// study(){

// }
// }
// let fat = new Father()
// let res = fat as Son 将father类型转换为son类型
// 什么是类型转换
// let res = <Son>fat
// res.
// let son = new Son()
// let res2 = son as Father
// res2.
// new 的底层发生了什么

// 场景2 ab都是类 但是没有继承关系 转换后的类型必须包含转换前的类型的所有属性 转换之前的属性是转换之后的子集
// class A {
// public name:string
// }
// class B {
// public name:string
// public age:number
// }
// let a = new A()
// let res = a as B
// let b = new B()

// 场景3 A是类,b是接口,A类实现了B接口 implements
// interface B {
// username:string,
// age:number
// }
// class A implements B {
// public username: string
// public age: number
// }

// 场景4 A是类 B是接口 A类没有实现B接口

// 场景5 A是类 B是type定义的类型
// type B = {
// username:string,
// age:number
// }
// class A implements B {
// public username: string
// public age: number
// }

// 场景6 A是类 B是type定义的类型 A没有实现B定义的数据类型
// type B = {
// username:string,
// age:number
// }
// class A {
// public username: string
// public age: number
// }

// 场景7 A是一个函数上参数的联合类型变量
// function SS(name:string | number){
// let num = name as number + 3
// }

// 场景8 多个类组成的联合类型如何断言
// let a:A | B | C
// a as A
// a as B
// a as C

// 场景9 转换为any或者unknown类型

类型守卫
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
类型守卫需要了解一下new 的过程
class Person {
name
age
}
let per = new Person()

1:创建一个obj对象
var obj = {}
2:使创建对象的__proto__指向构造函数/类的prototype
obj.__proto = Person.prototype
3:借用构造函数/类的apply为obj对象添加name与age属性
Person.apply(obj,["1232",123])

类型守卫
在词句的块级作用域内,if语句内或者条目运算符表达式以内,缩小变量类型的一种类型推断的行为
类型守卫产生时机 获得更精准的变量类型,减少不必要的类型断言
类型判断 typeof
属性或方法判断 in 判断属性或者方法是不是在该对象中 "name" in obj "getname" in obj
实例判断 instanceof
字面量相等判断 == === != !==

typeof 局限性
检测范围 string | number | bigint | boolean | symbol | undefined | object | function
检测变量并不完全准确
typeof null 结果为object
typeof [] 结果为object

typeof替代方案
Object.prototype.toString.call(检测对象)
仍然无法解决自定义函数类,使用instanceof

instanceof
a instanceof b 可用于原型链继承关系 子类 instanceof 父类 左边是否是右边的实例对象 会一直在左边的实例对象的__proto__一直查找到Object的prototype空间

instanceof应用场景
class car {

}
class Bus {

}
car 和 Bus类中各自包含一个求总价的方法
class GetTotal {
returnTotal(cartype:car | Bus){
if(cartype instanceof car){
return car.total()
} else if(cartype instanceof Bus){
return Bus.total()
}
}
}
根据不同车类型返回价格

自定义守卫
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
自定义守卫
格式
function 函数名 (形参:参数类型/大多为any):形参 is A类型 = boolean + 类型守卫能力{
return true / false
}

function isString(str:any):str is string{
return typeof str === 'string'
}

function isFunc(str:any):str is Function{
return typeof str === 'function'
}



抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
抽象类
一个在任何位置都不能实例化的类就是一个抽象类,实例化无意义的类
包含0个或多个带有方法体的方法和不带有方法体的抽象方法
父类的抽象方法会在子类强制实现
abstract class A {
public name:string
public eat(){

}
public abstract run():void
}
class B extends A {
public run(): void {
console.log('1232')
}
}
let a = new A() // 抽象类无法实例化

抽象类扩大,适配器
后端称为适配器adapter
export {}
多态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
多态
定义:
父类的对象变量可以接收任何一个子类的对象
从而用这个父类的对象变量来调用子类中重写的方法而输出不同的结果

产生条件:
1:必须存在继承关系
2:必须有方法重写

利于项目拓展

无法调用子类独有方法,必须结合instanceif类型守卫解决
属性不必完全重叠,在继承的情况下
class Father {
name:string
eat(){
console.log('父亲吃饭')
}
}

class A extends Father{
age:number
eat(){
console.log('A')
}
}

class B extends Father {
address:string
eat(): void {
console.log('B')
}
}

let fat:Father = new B()


改写汽车返回价格
class Fat {
total(){

}
}
class car extends Fat {
total(){

}
}
class Bus extends Fat {
total(){

}
aaa(){

}
}
car 和 Bus类中各自包含一个求总价的方法
class GetTotal {
returnTotal(cartype: Fat){
return cartype.total() // 只能调用total方法 无法调用B类中独有的的aaa方法
}
}
let gettotal = new GetTotal()
let car = new car()
let bus = new Bus()
gettotal(car)
gettotal(bus)

新特性可变元祖
1
2
3
4
let arr:[number,string,string,string] = [123,'1','2','3']
let arr2:[number,string,...any[]] = [123,'2',123,'456']
let [username,age,...rest]:[username_:string,age_:number,...rest:any[]] = ["123",123,'456']
let [username,age,...rest,desc]:[username_:string,age_:number,...rest:any[],desc_:string] = ["123",123,'456','123']
泛型类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person<T>{
public List:Array<T>
constructor(){
this.List = []
}
add(element:T){
this.List.push(element)
}
}
let person = new Person<string>()

泛型的参数可以使A-Z的任一大写字母,也可以是语义化的单词

Object是所有类型的父类型,object只是对象类型
Object object unknown和any的区别

…….

泛型约束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
T extends object
表示具体化的类型只能是object类型


typeof obj能获得该对象的类型
let obj = {name:'',age:2}
console.log(typeof obj) //{name:string,age:number}

keyof K
keyof表示获取一个类或者一个对象类型,或者一个接口类型的所有属性名组成的联合类型
console.log(keyof 类型/也可以是类) //name | age 甚至可以获取到类上的方法名
keyof typeof obj


T extends keyof K
T是K联合类型中的某一个或者全部联合类型

let obj = {name:'',age:2}
type one = typeof obj //{name:string,age:number}
type two = keyof one // //name | age
type objKeyType = one[two] / type objKeyType = one['name'] //string || number 得到值的类型


泛型接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface List<T>{
add(ele:T):void;
get(index:number):T;
size():number;
remove(value:T):T
}

class Person<T> implements List <T>{
public add(){

}
......
}

let per = new Person<>()

如果类型是any,会很麻烦,无法判断返回值原型对象所携带的方法,无法智提示
一个父类数组对象变量里面的每一个元素都可以是任何一个该父类的子类对象

泛型函数
1
2
3
4
5
function fn<T>(arr:Array<T>):Array<T>{

}
泛型函数可以在调用返回后得到返回值的具体类型数据,从而可以有自动方法和属性的提示和错误编译提示

泛型函数重载

分工明确

1
2
3
4
5
function fn(data:string):string
function fn<T>(data:T):T[]
function fn(data:any):any{

}
泛型工厂函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
工厂函数类型定义
代表所有类【等价JS的构造函数】的函数类型
泛型工厂函数定义:一个可以创建任意类对象的通用函数
使用场景:
不方便或者没有办法直接new类名()格式来创建对象,在一些测试欧哲调试代码中简化代码使用

通用函数类型
type fn = (...args:any)=> any

interface fn {
(..args:any):any
}

function fnn:fn = () => {

}

let t = new Test() //在ts中不能直接new一个函数创建实例

type fn = new (...args) => Student学生类
type fn = new (...args) => Teacher老师类

更加具备通用性
type fn = new (...args) => any

工厂函数

泛型工厂函数

交叉类型
1
2
3
4
5
6
7
8
9
10
11
将多个类型合并【多个类型属性和方法的并集】成的类型就是交叉类型

type1 =
type2 =
type3 =
let obj:type1 & type2 & type3 =
交叉类型可以获取两个类型的任意变量和方法,联合类型只能获取共有属性和方法


应用场景

infer
1
2
infer表示在extends条件语句中以占位符出现的用来修饰数据类型的关键字,被修饰的数据类型等到使用时才能被推断出来

ts高级类型

extract

1

exclude

1

record

1

pick

1

partial

required

readonly

omit