发布于 

TypeScript

定位:TypeScript是JavaScript的超集(功能更强大(?),语法更易懂和简洁(?))
运行过程:.ts文件编译成.js文件【TypeScript编译器或Babel转译】
目标:开发大型项目

类型

枚举 Enum

关键字:枚举
枚举类型用于定义数值集合。

1
2
3
enum Color {Red, Green, Blue};
let c: Color = Color.Blue;
console.log(c); // 输出 2

元组 Tuple

关键字:无
元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。

1
2
3
4
let x: [string, number];
x = ['Runoob', 1]; // 运行正常
x = [1, 'Runoob']; // 报错
console.log(x[0]); // 输出 Runoob

never

关键字:never
never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。这意味着声明为 never 类型的变量只能被 never 类型所赋值,在函数中它通常表现为抛出异常或无法执行到终止点(例如无限循环)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let x: never;
let y: number;

// 运行错误,数字类型不能转为 never 类型
x = 123;

// 运行正确,never 类型可以赋值给 never类型
x = (()=>{ throw new Error('exception')})();

// 运行正确,never 类型可以赋值给 数字类型
y = (()=>{ throw new Error('exception')})();

// 返回值为 never 的函数可以是抛出异常的情况
function error(message: string): never {
throw new Error(message);
}

// 返回值为 never 的函数可以是无法被执行到的终止点的情况
function loop(): never {
while (true) {}
}

无返回值 void

定义一个方法时,方法没有返回任何值

1
2
3
function run():void{
console.log('run')
}

null undefined

1
2
let num:number | null | undefined
// 可以是多类型

任意类型 any

1
2
3
4
5
6
// 用法一
let oBox:any = document.getElementById('box')
// 返回的是object
// 但是ts中没有object类型
// 所以此处用any合适
oBox.style.color = 'red'

boolean

number

string

array

类 Class

类里面的修饰符

  • public 都可访问
  • private 自身内部可访问
  • protected 自身及子类内部可访问
    属性如果不加修饰符,默认就是public

    抽象类和抽象方法

    定义一个标准
  • 抽象方法必须在抽象类里面
  • 抽象类不能被直接实例化
  • 抽象类的子类必须实现抽象类里的抽象方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    abstract class Animal{
    name:string;
    constructor(name:string){
    this.name = name
    }
    abstract eat():any
    }

    const animal = new Animal() // 会报错

    class dog extends Animal{
    constructor(name:string){
    super(name)
    }
    eat(){
    console.log(this.name+'吃粮食')
    }
    }

    const d = new dog('旺旺')
    d.eat()

多态和继承

  • 子类可重写父类中的方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class Animal{
    name:string;
    constructor(name:string){
    this.name = name
    }
    eat():any{
    console.log('吃方法')
    }
    }

    class dog extends Animal{
    constructor(name:string){
    super(name)
    }
    eat(){
    console.log(this.name+'吃粮食')
    }
    }

    const d = new dog('旺旺')
    d.eat()

    接口 Interface

  • 接口中所有的属性都不能有实际的值
  • 接口只定义对象的结果而不考虑实际值
  • 接口中所有方法都是抽象方法(没有方法体)
  • 定义类(class)时,可以实现(implements)一个接口
  • 接口对类起到限制作用,定义一个标准

    约束json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /*
    约束json
    */
    interface person = {
    name:string;
    age:number;
    loveStudy?:boolean;// 可选
    [propName:string]:any;// 字段概括?
    }

    约束方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /*
    函数类型接口
    约束方法
    */
    interface encrypt{
    (key:string,value:string):string;
    }

    const md5:encrypt = function(key:string,value:string):string{
    return key+value
    }

    可索引接口:数组、对象的约束(不常用)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /*
    可索引接口:数组、对象的约束(不常用)
    数组
    */
    interface userArr{
    [index:number]:string
    }
    const arr:userArr = ['aaa','bbb']
    // 对象
    interface userObj{
    [index:string]:string
    }
    const obj:userObj = {name:'张三'}

    对类的约束

    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
    /*
    对类的约束
    */
    interface Animal{
    name:string;
    eat(str:string):void;
    }
    class dog implements Animal{
    name:tring;
    constructor(name:string){
    this.name = name
    }
    eat(){
    console.log(this.name+'吃骨头')
    }
    }

    const d = new Dog('旺旺')
    d.eat()

    class cat implements Animal{
    name:tring;
    constructor(name:string){
    this.name = name
    }
    eat(food:string){
    console.log(this.name+'eat')
    }
    }
    const c = new Cat('小花猫')
    c.eat('菜')

    接口扩展

    接口可以继承接口
    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
    /*
    接口扩展
    接口可以继承接口
    */
    interface Animal{
    eat():void;
    }
    interface People extends Animal{
    work():void;
    }
    class Programmer{
    name:string;
    constructor(name:string){
    this.name = name
    }
    coding(code:string){
    console.log(this.name+code)
    }
    }
    class Web extends Programmer implements People{
    constructor(name:string){
    supper(this.name)
    }
    eat(){
    console.log(this.name+'喜欢吃馒头')
    }
    work(){
    console.log(this.name+'写代码')
    }
    }
    const w = new Web('小李')
    w.eat()
    w.work()
    w.coding('write typescript')

    类型 Type

    和接口差不多,但是不能重复定义
    1
    2
    3
    4
    5
    type person = {name:string,age:number}
    const personArr:person[] = [
    {name:'张三',age:10},
    {name:'李四',age:20}
    ]

    其他

    交叉类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    interface IPerson {
    id: string;
    age: number;
    }

    interface IWorker {
    companyId: string;
    }

    type IStaff = IPerson & IWorker;

    const staff: IStaff = {
    id: 'E1006',
    age: 33,
    companyId: 'EFT'
    };

    console.dir(staff)
    在上面示例中,我们首先为 IPerson 和 IWorker 类型定义了不同的成员,然后通过 & 运算符定义了 IStaff 交叉类型,所以该类型同时拥有 IPerson 和 IWorker 这两种类型的成员。

泛型

遇到类型不明确时可以使用泛型
解决类、接口、方法的复用性,以及对不特定数据类型的支持
泛型函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
T表示泛型:具体什么类型是调用这个方法的时候决定的
*/
function fn<T>(a:T):T{
return a
}

let res = fn(10) // 不指定泛型
let res2 = fn<string>('hello')// 指定泛型,降低编译出错率

// 泛型可以同时指定多个
function fn2<T,K>(a:T,b:K):T{
console.log(b)
return a
}

fn2<number,string>(10,'hello')

泛型类

1
2
3
4
5
6
7
8
9
// 类
class MyClass<T>{
name: T;
constructor(name:T){
this.name = name
}
}
const mc = new MyClass<string>('孙悟空')
const mco = new MyClass<number>(66)

泛型接口

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
// 方法一
interface ConfigFn{
<T>(value:T):T;
}
const getData:ConfigFn = function<T>(value:T):T{
return value
}

getData<string>('aaa')
getData<number>(123)

// 方法二
function getData<T>(value:T):T{
return value
}
const myGetData:ConfigFn<string> = getData;
myGetData('aaa')

// 泛型继承接口
interface Inter{
length:number
}

function fn3<T extends Inter>(a:T):number{
return a.length
}

编译时自动判断出类型T
类型名字“T”随意

应用举例 - 数据库相关

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
interface DBI<T>{
add(info:T):boolean;
update(info:T,id:number):boolean;
delete(id:number):boolean;
get(id:number):any[];
}

// 定义一个操作mysql数据库的类
// 注意:要实现泛型接口 这个类也应该是一个泛型类
class MysqlDb<T> implements DBI<T>{
add(info:T):boolean{

}
update(info:T,id:number):boolean{

}
delete(id:number):boolean{

}
get(id:number):any[]{

}
}
// 定义一个操作mssql数据库的类
class MsSqlDb<T> implements DBI<T>{
add(info:T):boolean{

}
update(info:T,id:number):boolean{

}
delete(id:number):boolean{

}
get(id:number):any[]{
const list = [
{title:'11',desc:'11'},
{title:'111',desc:'111'},
]
return list
}
}

// 操作用户表 定义一个User类和数据表做映射
class User{
username:string | undefined;
password:string | undefined;
}
const u = new User()
u.username = 'zhangsan'
u.password = 'zhangsan'
const oMysql = new MysqlDb<User>()
oMysql.add(u)
// 换mssql数据库
const oMsql = new MsSqlDb<User>()
oMsql.add(u)

模块和命名空间

命名空间:内部模块,组织代码,避免命名冲突
一个模块可以包含多个命名空间

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
namespace A{
interface Animal{
name:string;
eat(str:string):void;
}
export class dog implements Animal{
name:tring;
constructor(name:string){
this.name = name
}
eat(){
console.log(this.name+'吃骨头')
}
}
}
// 内部引用
const d = new A.Dog('旺旺')
d.eat()


// 外部文件引用
// namespace需要export
export namespace A{
// 代码同上
}
import {A} from '...'
const d = new A.Dog('旺旺')
d.eat()

装饰器

装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。
通俗的讲装饰器就是一个方法。
常见装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器。
装饰器的写法:普通装饰器(无法传参),装饰器工厂(可传参)。

类装饰器

用于类构造函数,可以用来监视,修改或替换类定义,传入一个参数。

普通装饰器(无法传参)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*普通装饰器*/
function logClass(params:any){
console.log(params)
// params就是当前实例
params.prototype.apiUrl = 'xxx'
params.prototype.run = function(){
console.log('run')
}
}

@logClass
class HttpClient{
constructor(){

}
getData(){

}
}

const http = new HttpClient();
console.log(http.apiUrl) // xxx
http.run() // 'run'
装饰器工厂(可传参)
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
function logClass(params:string){
return function(target:any){
console.log(target) // 当前HttpClient实例
console.log(params) // 'http://www.xxx.com/api'
// params就是当前类
target.prototype.apiUrl = params
target.prototype.run = function(){
console.log('run')
}
}
}

@logClass('http://www.xxx.com/api')
class HttpClient{
constructor(){

}
getData(){

}
}

const http = new HttpClient();
console.log(http.apiUrl) // 'http://www.xxx.com/api'
http.run() // 'run'
修改类的构造函数和方法
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
function logClass(target:any){
console.log(target)
return class extends target{
apiUrl:any = '我是修改后的数据'
getData(){
this.apiUrl = this.apiUrl+'---'
console.log(this.apiUrl)
}
}
}

@logClass
class HttpClient{
apiUrl:string | undefined;
constructor(){
this.apiUrl = '我是构造函数里面的apiUrl'
}
getData(){
console.log(this.apiUrl)
}
}

const http = new HttpClient();
console.log(http.apiUrl) // '我是修改后的数据'
http.getData() // '我是修改后的数据---'

属性装饰器

给属性赋值…

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
function logClass(params:string){
return function(target:any){
console.log(target) // 当前HttpClient实例
console.log(params) // 'xxx'
// params就是当前类
target.prototype.apiUrl = params
target.prototype.run = function(){
console.log('run')
}
}
}

function logProperty(params:any){
console.log(target)
return function(target:any,attr:any){
console.log(params) // 'http://www.xxx.com/api'
console.log(target) // 当前HttpClient实例
console.log(attr) // url
// 修改属性值
target[attr] = params
}
}

@logClass('xxx')
class HttpClient{
@logProperty('http://www.xxx.com/api')
url:string | undefined;
constructor(){
this.apiUrl = '我是构造函数里面的apiUrl'
}
getData(){
console.log(this.url)
}
}

方法装饰器

修改当前实例的属性和方法,也可以的替换当前被装饰方法

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
function logMethod(params:any){
return function(target:any,methodName:any,desc:any){
console.log(params) // 'http://www.xxx.com/api'
console.log(target) // 当前HttpClient实例
console.log(methodName) // 方法名称
console.log(desc.value) // 方法具体描述
// 修改装饰器方法 把装饰器方法里面传入的所有参数改为string类型
// 保存原来方法
const oMethod = desc.value
desc.value = function(...args:any[]){
args.map(value=>{
return String(value)
})
console.log(args) // ['123','xxx']
oMethod.apply(this,args) // 同时调用原来的getData方法
}
}
}
class HttpClient{
url:string | undefined;
constructor(){

}
@get('http://www.xxx.com/api')
getData(){
console.log(this.url)
}
}

const http:any = new HttpClient();
http.getData(123,'xxx') // 'run'

方法参数装饰器

扩展以及修改当前类的属性及方法

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
function logParams(params:any){
return function(target:any,methodName:any,paramsIndex:any){
console.log(params) // 'xxx'
console.log(target) // 当前HttpClient实例
console.log(methodName) // 方法名称 getData
console.log(paramsIndex) // 方法具体描述 0

target.apiUrl = params
}
}

class HttpClient{
url:string | undefined;
constructor(){

}

getData(@logParams('xxx') uuid:any){
console.log(this.url)
}
}

const http:any = new HttpClient();
http.getData(123456) // 'run'
console.log(http.apiUrl) // 'xxx'

装饰器执行顺序

属性 -> 方法 -> 方法参数 -> 类
如果有多个同种装饰器,从后往前执行

结合Vue3

reactive

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
<script setup lang='ts'>
import { reactive } from 'vue'
interface User{
name:string;
age:number;
getInfo():void;
}
// 方法一
const u:User = reactive({
name:'zhangsan',
age:18,
getInfo(){
console.log(this.name+this.age+'岁了')
}
})
// 方法二
const u = reactive<User>({
name:'zhangsan',
age:18,
getInfo(){
console.log(this.name+this.age+'岁了')
}
})
// 方法三
const u = reactive({
name:'zhangsan',
age:18,
getInfo(){
console.log(this.name+this.age+'岁了')
}
}) as User
</script>

ref

1
2
3
4
5
<script setup lang='ts'>
import { ref } from 'vue'
let u = ref<number|string>(20)
u.value = '20'
</script>

computed

1
2
3
4
5
6
7
<script setup lang='ts'>
import { computed } from 'vue'
const username = 'zhangsan'
let reverseUsername = computed(():string=>{
return username.split('').reverse().join('')
})
</script>