TypeScript学习

文档

文档说明

  1. 学习地址  
  2. 快速上手
  3. 代码测试
  4. 安装
1
2
3
>npm install -g typescript   安装
>tsc -v     查看版本Version 4.6.4
>tsc --init  生成tsconfig.json配置文件

开始

编译

新建ts文件

src/helloworld.ts

1
2
3
4
5
function greeter (person) {
return 'Hello, ' + person
}
let user = 'Yee'
console.log(greeter(user))

>tsc helloworld.ts 编译
>node helloworld.js 运行
hello,Yee 打印

vscode自动编译

1
2
3
4
5
6
7
1). 生成配置文件tsconfig.json
tsc --init
2). 修改tsconfig.json配置
"outDir": "./js",
"strict": false,
3). 启动监视任务:
终端 -> 运行任务 -> 监视tsconfig.json

终端任务重用
e273c55cd0693cc35d379d5a6791b045.png

如果还是失败,vscode编辑器要选择默认终端

配置中文提示
配送搜索 typescript local 设置成zh-CN

编译选项

基础类型

 > 基本语法
 
let 变量名: 数据类型 = 值

 

1
2
3
4
 let a2: number = 0b1010  // 二进制
// 类型包括 :
// number string undefined boolean object
// any 任何变量

数组类型

语法:
let 变量名: 数据类型[] = [值1,值2,值3]
语法:
let 变量名: Array<数据类型> = [值1,值2,值3]

1
2
 let arr1: number[] = [10, 20, 30, 40, 50]
let arr2: Array<number> = [100, 200, 300]

枚举类型

枚举里面的每个数据值都可以叫元素,每个元素都有自己的编号,编号是从0开始的,依次的递增加1

1
2
3
4
5
6
7
8
9
10
  enum Color {
    red = 1,
    green,
    blue
  }
  // 定义一个Color的枚举类型的变量来接收枚举的值
  let color: Color = Color.red
  console.log(color)  //1 
  console.log(Color.red, Color.green, Color.blue) // 1,2,3
  console.log(Color[3]) // "blue" 

void 类型

在函数声明的时候,小括号的后面使用:void,代表的是该函数没有任何的返回值

1
2
3
4
5
6
7
  function showMsg(): void {
    console.log('你好')
    // return
    // return undefined
    return null //报错
  }
  console.log(showMsg())

object 类型

定义一个函数,参数是object类型,返回值也是object类型

1
2
3
4
5
6
7
8
9
10
11
  function getObj(obj: object): object {
    console.log(obj)
    return {
      name'卡卡西',
      age27
    }
  }
  console.log(getObj(String))
  //function String() { [native code] } 
  // {"name": "卡卡西","age": 27 } 

联合类型,类型断言,类型推断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
类型断言(Type Assertion): 可以用来手动指定一个值的类型
语法:
    方式一: <类型>值
    方式二: 值 as 类型  tsx中只能用这种方式
*/
/* 需求: 定义一个函数得到一个字符串或者数值数据的长度 */
function getLength(x: number | string) {
  if ((x as string).length) {
    return (x as string).length
  } else {
    return x.toString().length
  }
}
console.log(getLength('abcd'), getLength(1234))

接口

接口是对象的状态(属性)和行为(方法)的抽象(描述)
接口:是一种类型,是一种规范,是一种规则,是一个能力,是一种约束

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
  // 需求: 创建人的对象, 需要对人的属性进行一定的约束
  // id是number类型, 必须有, 只读的
  // name是string类型, 必须有
  // age是number类型, 必须有
  // sex是string类型, 可以没有
  // 定义一个接口,该接口作为person对象的类型使用,限定或者是约束该对象中的属性数据
  interface IPerson {
    // readonly id是只读的,是number类型,const修饰属性,想要设置该属性是只读的,是不能使用的
    readonly id:  number
    name: string
    age: number
    // ? 可有可无的
    sex?: string
//定义了任意属性取 string 类型的值
[proppName:string]:any;
  }
  // 定义一个对象,该对象的类型就是我定义的接口IPerson
  const person: IPerson = {
    id: 1,
    name: '小甜甜',
    age: 18,
    // sex这个属性没有也是可以的
    // sex: '女'
  }
  console.log(person)
  // id属性此时是可读可写
  // person.id = 100
  person.sex='女'
  // person.money = '123131'
  console.log(person)

函数类型

 为了使用接口表示函数类型,我们需要给接口定义一个调用签名。

它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  // 函数类型:通过接口的方式作为函数的类型来使用
  // 定义一个接口,用来作为某个函数的类型使用
  interface ISearchFunc {
    // 定义一个调用签名
    (source: string, subString: string): boolean
  }
  // 定义一个函数,该类型就是上面定义的接口
  const searchString: ISearchFunc = function (source: string, subString: string): boolean {
    // 在source字符串中查找subString这个字符串
    return source.search(subString) > -1
  }
  // 调用函数
  console.log(searchString('哈哈,我又变帅了', '帅'))
//"Hello, Yee Huang"
//true

类类型(约束)

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

  // 定义一个接口
  interface IFly {
    // 该方法没有任何的实现(方法中什么都没有)
    // fly()
    fly:Function
  }
  // 定义一个类,这个类的类型就是上面定义的接口(实际上也可以理解为,IFly接口约束了当前的这个Person类)
  class Person implements IFly {
    // 实现接口中的方法
    fly() {
      console.log('我会飞了,我是超人')
    }
  }
  // 实例化对象
  const person = new Person()
  person.fly() //"我会飞了,我是超人"
  // 定义一个接口
  interface ISwim {
    swim:Function
    // swim()
  }
  // 定义一个类,这个类的类型就是IFly和ISwim(当前这个类可以实现多个接口,一个类同时也可以被多个接口进行约束)
  class Person2 implements IFly, ISwim {
    fly() {
      console.log('我飞了2')
    }
    swim() {
      console.log('我会游泳啦2')
    }
  }
  // 实例化对象
  const person2 = new Person2()
  person2.fly() //"我飞了2"
  person2.swim() //"我会游泳啦2"
  // 总结: 类可以通过接口的方式,来定义当前这个类的类型
  // 类可以实现一个接口,类也可以实现多个接口,要注意,接口中的内容都要真正的实现
  // 定义了一个接口,继承其他的多个接口
  interface IMyFlyAndSwim extends IFly, ISwim { }
  // 定义一个类,直接实现IMyFlyAndSwim这个接口
  class Person3 implements IMyFlyAndSwim {
    fly() {
      console.log('我飞了3')
    }
    swim() {
      console.log('我会游泳啦3')
    }
  }
  const person3 = new Person3()
  person3.fly() //"我飞了3"
  person3.swim() //我会游泳啦3

  // 总结:接口和接口之间叫继承(使用的是extends关键字),类和接口之间叫实现(使用的是implements)


建一个 User 类,它带有一个构造函数和一些公共字段。因为类的字段包含了接口所需要的字段,所以他们能很好的兼容。

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

// 类:可以理解为模版,通过模版可以实例化对象
// 面向对象的编程思想

class User {
fullName: string
firstName: string
lastName: string

constructor (firstName: string, lastName: string) {
this.firstName = firstName
this.lastName = lastName
this.fullName = firstName + ' ' + lastName
}}

interface Person {
firstName: string
lastName: string}

function greeter (person: Person) {
return 'Hello, ' + person.firstName + ' ' + person.lastName
}

let user = new User('Yee', 'Huang')

console.log(greeter(user)) //"Hello, Yee Huang"

继承

继承:类与类之间的关系
继承后类与类之间的叫法:
A类继承了B这个类,那么此时A类叫子类,B类叫基类
子类—->派生类
基类—->超类(父类)
一旦发生了继承的关系,就出现了父子类的关系(叫法)

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
 // 定义一个类,作为基类(超类/父类)
  class Person {
    // 定义属性
    name: string // 名字
    age: number // 年龄
    gender: string // 性别
    // 定义构造函数
    constructor(name: string='小明', age: number=18, gender: string='男') {
      // 更新属性数据
      this.name = name
      this.age = age
      this.gender = gender
    }
    // 定义实例方法
    sayHi(str: string) {
      console.log(`我是:${this.name},${str}`)
    }
  }
  // 定义一个类,继承自Person
  class Student extends Person {
    constructor(name: string, age: number, gender: string) {
      // 调用的是父类中的构造函数,使用的是super
      super(name, age, gender)
    }
    // 可以调用父类中的方法
    sayHi() {
      console.log('我是学生类中的sayHi方法')
      // 调用父类中的sayHi方法
      super.sayHi('哈哈')
    }
  }
  // 实例化Person
  const person = new Person('大明明',89,'男')
  person.sayHi('嘎嘎')
  //打印: "我是:大明明,嘎嘎"
  // 实例化Student
  const stu = new Student('小甜甜',16,'女')
  stu.sayHi()
  //打印: "我是学生类中的sayHi方法"
  //打印: "我是:小甜甜,哈哈"
  // 总结:类和类之间如果要有继承关系,需要使用extends关键字
  // 子类中可以调用父类中的构造函数,使用的是super关键字(包括调用父类中的实例方法,也可以使用super)
  // 子类中可以重写父类的方法

多态

多态:父类型的引用指向了子类型的对象,不同类型的对象针对相同的方法,产生了不同的行为

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
 // 定义一个父类
  class Animal {
    // 定义一个属性
    name: string
    // 定义一个构造函数
    constructor(name: string) {
      // 更新属性值
      this.name = name
    }
    // 实例方法
    run(distance: number = 0) {
      console.log(`跑了${distance} 米这么远的距离`, this.name)
    }
  }
  // 定义一个子类
  class Dog extends Animal {
    // 构造函数
    constructor(name: string) {
      // 调用父类的构造函数,实现子类中属性的初始化操作
      super(name)
    }
    // 实例方法,重写父类中的实例方法
    run(distance: number = 5) {
      console.log(`跑了${distance} 米这么远的距离`, this.name)
    }
  }
  // 定义一个子类
  class Pig extends Animal {
    // 构造函数
    constructor(name: string) {
      // 调用父类的构造函数,实现子类中属性的初始化操作
      super(name)
    }
    // 实例方法,重写父类中的实例方法
    run(distance: number = 10) {
      console.log(`跑了${distance} 米这么远的距离`, this.name)
    }
  }
  // 实例化父类对象
  const ani: Animal = new Animal('动物')
  ani.run() //"跑了0 米这么远的距离",  "动物"
  // 实例化子类对象
  const dog: Dog = new Dog('大黄')
  dog.run() //"跑了5 米这么远的距离",  "大黄"
  // 实例化子类对象
  const pig: Pig = new Pig('八戒')
  pig.run() //"跑了10 米这么远的距离",  "八戒"
  console.log('===========')
  // 父类和子类的关系:父子关系,此时,父类类型创建子类的对象
  const dog1: Animal = new Dog('小黄')
  dog1.run() // "跑了5 米这么远的距离",  "小黄"  
  const pig1: Animal = new Pig('小猪')
  pig1.run() //  "跑了10 米这么远的距离",  "小猪"
  console.log('=============')
  // 该函数需要的参数是Animal类型的
  function showRun(ani: Animal) {
    ani.run()
  }
  showRun(dog1) //"跑了5 米这么远的距离",  "小黄"
  showRun(pig1) //"跑了10 米这么远的距离",  "小猪"

修饰符

修饰符(类中的成员的修饰符):主要是描述类中的成员(属性,构造函数,方法)的可访问性
类中的成员都有自己的默认的访问修饰符,public
public修饰符—公共的,类中成员默认的修饰符,代表的是公共的,任何位置都可以访问类中的成员
private修饰符—私有的,类中的成员如果使用private来修饰,那么外部是无法访问这个成员数据的,当然,子类中也是无法访问该成员数据的
protected修饰符—-受保护的,类中的成员如果使用protected来修饰,那么外部是无法访问这个成员数据的,当然,子类中是可以访问该成员数据的

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

// 定义一个类
  class Person {
    // 属性 public 修饰了属性成员
    // public name: string
    // 属性 private 修饰了属性成员
    // private name: string
    // 属性protected 修饰了属性成员
    protected name:string
    // 构造函数
    public constructor(name: string) {
      // 更新属性
      this.name = name
    }
    // 方法
    public eat() {
      console.log('嗯,这个骨头真好吃', this.name)
    }
  }
  // 定义一个子类
  class Student extends Person {
    // 构造函数
    constructor(name: string) {
      super(name)
    }
    play() {
      console.log('我就喜欢玩布娃娃',this.name)
    }
  }
  // 实例化对象
  const per = new Person('大蛇丸')
  // 类的外部可以访问类中的属性成员
  // console.log(per.name)
  per.eat()  //"嗯,这个骨头真好吃",  "大蛇丸"
  const stu = new Student('红豆')
  stu.play() //"我就喜欢玩布娃娃",  "红豆"
  // console.log(stu.name)

readonly修饰符

readonly修饰符:首先是一个关键字,对类中的属性成员进行修饰,修饰后,该属性成员,就不能在外部被随意的修改了
构造函数中,可以对只读的属性成员的数据进行修改
如果构造函数中没有任何的参数,类中的属性成员此时已经使用readonly进行修饰了,那么外部也是不能对这个属性值进行更改的
构造函数中的参数可以使用readonly进行修饰,一旦修饰了,那么该类中就有了这个只读的成员属性了,外部可以访问,但是不能修改
构造函数中的参数可以使用public及privte和protected进行修饰,无论是哪个进行修饰,该类中都会自动的添加这么一个属性成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  // readonly修饰类中的成员属性操作
  // 定义一个类型
  class Person {
    // 属性
    // readonly name: string='大甜甜' // 初始值
    readonly name: string
    // 构造函数
    constructor(name: string = '大甜甜') {
      this.name = name
    }
    sayHi() {
      console.log('考尼奇瓦', this.name)
      // 类中的普通方法中,也是不能修改readonly修饰的成员属性值
      // this.name = '大甜甜'
    }
  }
  // 实例化对象
  const person: Person = new Person('小甜甜')
  console.log(person)
  console.log(person.name) //小甜甜
 // 此时无法修改,因为name属性是只读的
  person.name = '大甜甜' //报错
  console.log(person.name)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  // 定义一个类型
  class Person {
    // 构造函数
    // 构造函数中的name参数,一旦使用readonly进行修饰后,那么该name参数可以叫参数属性
    // 构造函数中的name参数,一旦使用readonly进行修饰后,那么Person中就有了一个name的属性成员
    // 构造函数中的name参数,一旦使用readonly进行修饰后,外部也是无法修改类中的name属性成员值的
    constructor(readonly name: string = '大甜甜') { //public private protected
      this.name = name
    }
    // 构造函数中的name参数,一旦使用public进行修饰后,那么Person类中就有了一个公共的name属性成员了
    // 构造函数中的name参数,一旦使用private进行修饰后,那么Person类中就有了一个私有的name属性成员了
    // 构造函数中的name参数,一旦使用protected进行修饰后,那么Person类中就有了一个受保护的name属性成员了(只能在本类和派生类中访问及使用)
  }
  // 实例化对象
  const person: Person = new Person('小甜甜')
  console.log(person)
  person.name = '佐助' //报错
  console.log(person.name)

存储器

存取器:让我们可以有效的控制对 对象中的成员的访问,通过getters和setters来进行操作

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
// 外部可以传入姓氏和名字数据,同时使用set和get控制姓名的数据,外部也可以进行修改操作
  class Person {
    firstName: string // 姓氏
    lastName: string // 名字
    constructor(firstName: string, lastName: string) {
      this.firstName = firstName
      this.lastName = lastName
    }
    // 姓名的成员属性(外部可以访问,也可以修改)
    // 读取器----负责读取数据的
    get fullName() {
      console.log('get中...')
      // 姓名====>姓氏和名字的拼接
      return this.firstName + '_' + this.lastName
    }
    // 设置器----负责设置数据的(修改)
    set fullName(val) {
      console.log('set中...')
      // 姓名---->把姓氏和名字获取到重新的赋值给firstName和lastName
      let names = val.split('_')
      this.firstName = names[0]
      this.lastName = names[1]
    }
  }
  // 实例化对象
  const person: Person = new Person('东方', '不败')
  console.log(person)
  // 获取该属性成员属性
  console.log(person.fullName)
  // 设置该属性的数据
  person.fullName = '诸葛_孔明'
  console.log(person.fullName)

静态成员

静态成员:在类中通过static修饰的属性或者方法,那么就是静态的属性及静态的方法,也称之为:静态成员
静态成员在使用的时候是通过类名.的这种语法来调用的

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

  // 定义一个类
  class Person {
    // 类中默认有一个内置的name属性,所以呢,此时会出现错误的提示信息
    // 静态属性
    static name1: string = '小甜甜'
    // 构造函数是不能通过static来进行修饰的
    constructor() {
      // 此时this是实例对象,name1是静态属性,不能通过实例对象直接调用静态属性来使用
      // this.name1 = name
    }
    // 静态方法
    static sayHi() {
      console.log('萨瓦迪卡')
    }
  }
  // 实例化对象
  // const person: Person = new Person()
  // 通过实例对象调用的属性(实例属性)
  // console.log(person.name1)
  // 通过实例对象调用的方法(实例方法)
  // person.sayHi()
  // 通过类名.静态属性的方式来访问该成员数据
  console.log(Person.name1) // "小甜甜"
  // 通过类名.静态属性的方式来设置该成员数据
  Person.name1 = '佐助'
  console.log(Person.name1) //"佐助"
  // 通过类名.静态方法的方式来调用内部的静态的方法
  Person.sayHi() // "萨瓦迪卡"

抽象类

抽象类:包含抽象方法(抽象方法一般没有任何的具体内容的实现),也可以包含实例方法,抽象类是不能被实例化,为了让子类进行实例化及实现内部的抽象方法
抽象类的目的或者是作用最终都是为子类服务的

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
  // 定义一个抽象类
  abstract class Animal{
    // 抽象属性
    // abstract name:string
    // 抽象方法
    abstract eat()
    // 报错的,抽象方法不能有具体的实现
    // abstract eat(){
    //   console.log('趴着吃,跳着吃')
    // }
    // 实例方法
    sayHi(){
      console.log('您好啊')
    }
  }
  // 定义一个子类(派生类)Dog
  class Dog extends Animal{
    // name:string='小黄'
    // 重新的实现抽象类中的方法,此时这个方法就是当前Dog类的实例方法了
    eat(){
      console.log('舔着吃,真好吃')
    }
  }
  // 实例化Dog的对象
  const dog:Dog = new Dog()
  dog.eat()
  // 调用的是抽象类中的实例方法
  dog.sayHi()
  // console.log(dog.name)
  // 不能实例化抽象类的对象
  // const ani:Animal = new Animal()

函数

函数:封装了一些重复使用的代码,在需要的时候直接调用即可

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

  // js中的书写方式----->在ts中同样的可以这么写
  // 函数声明,命名函数
  // function add(x, y) { // 求和的函数
  //   return x + y
  // }
  // 函数表达式,匿名函数
  // const add2 = function (x, y) {
  //   return x + y
  // }
  // ts中的书写方式
  // 函数声明,命名函数
  // 函数中的x和y参数的类型都是string类型的,小括号后面的:string,代表的是该函数的返回值也是string类型的
  function add(x: string, y: string): string { // 求和的函数
    return x + y
  }
  const result1: string = add('111', '222')
  console.log(result1)
  console.log()
  // 函数表达式,匿名函数
  // 函数中的x和y参数的类型都是number类型的,小括号后面的:number,代表的是该函数的返回值也是number类型的
  const add2 = function (x: number, y: number): number {
    return x + y
  }
  console.log(add2(10, 20))
  // 函数的完整的写法
  // add3---->变量名--->函数add3
  // (x: number, y: number) => number 当前的这个函数的类型
  // function (x: number, y: number): number { return x+y }  就相当于符合上面的这个函数类型的值
  const add3: (x: number, y: number) => number = function (x: number, y: number): number {
    return x+y
  }
  console.log(add3(10,100))

参数

可选参数:函数在声明的时候,内部的参数使用了?进行修饰,那么就表示该参数可以传入也可以不用传入,叫可选参数
默认参数:函数在声明的时候,内部的参数有自己的默认值,此时的这个参数就可以叫默认参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

  // 定义一个函数:传入姓氏和名字,可以得到姓名(姓氏+名字=姓名)
  // 需求:如果不传入任何内容,那么就返回默认的姓氏
  // 需求:如果只传入姓氏,那么就返回姓氏
  // 需求:如果传入姓氏和名字,那么返回来的就是姓名
  const getFullName = function (firstName: string='东方', lastName?: string): string {
    // 判断名字是否传入了
    if (lastName) {
      return firstName + '_' + lastName
    } else {
      return firstName
    }
  }
  // 函数调用
  // 什么也不传入
  console.log(getFullName())  //"东方"
  // 只传入姓氏
  console.log(getFullName('诸葛')) //"诸葛"
  // 传入姓氏和名字
  console.log(getFullName('诸葛','孔明')) // "诸葛_孔明"

1
2
3
4
5
6
7
8
9
10
11
12
// 剩余参数(rest参数)
// 剩余参数是放在函数声明的时候所有的参数的最后
(() => {
  // ...args:string[] ---->剩余的参数,放在了一个字符串的数组中,args里面
  function showMsg(str: string,str2:string, ...args: string[]) {
    console.log(str) // a
    // console.log(str2) // b
    console.log(args) // b ,c ,d ,e
  }
  showMsg('a','b','c','d','e')
})()

函数重载

函数重载:函数名字相同,函数的参数及个数不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  // 定义一个函数
  // 需求: 我们有一个add函数,它可以接收2个string类型的参数进行拼接,也可以接收2个number类型的参数进行相加
  // 函数重载声明
  function add(x: string, y: string): string
  function add(x: number, y: number): number
  // 函数声明
  function add(x: string | number, y: string | number): string | number | undefined {
    if (typeof x === 'string' && typeof y === 'string') {
      return x + y // 字符串拼接
    } else if (typeof x === 'number' && typeof y === 'number') {
      return x + y // 数字相加
    }
    return
  }
  // 函数调用
  // 两个参数都是字符串
  console.log(add('诸葛', '孔明'))
  // 两个参数都是数字
  console.log(add(10, 20))
  // 此时如果传入的是非法的数据,ts应该给我提示出错误的信息内容,报红色错误的信息
  // console.log(add('真香', 10))
  // console.log(add(100, '真好'))

泛型

在定义函数、接口、类的时候不能预先确定要使用的数据的类型,而是在使用函数、接口、类的时候才能确定数据的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
  // 需求:定义一个函数,传入两个参数,第一参数是数据,第二个参数是数量
  // 函数的作用:根据数量产生对应个数的数据,存放在一个数组中
  // 定义一个函数
  function getArr1(value: number, count: number): number[] {
    // 根据数据和数量产生一个数组
    const arr: number[] = []
    for (let i = 0; i < count; i++) {
      arr.push(value)
    }
    return arr
  }
  const arr1 = getArr1(100.1233)
  console.log(arr1) // [100.123, 100.123, 100.123] 
1
2
3
4
5
6
7
8
9
10
11
12

 // 定义一个函数,同上,只不过传入的是字符串类型
  function getArr2(value: string, count: number): string[] {
    // 根据数据和数量产生一个数组
    const arr: string[] = []
    for (let i = 0; i < count; i++) {
      arr.push(value)
    }
    return arr
  }
  const arr2 = getArr2('abc'3)
  console.log(arr2) //["abc", "abc", "abc"] 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

 // 需求:可以传入任意类型的数据,返回来的是存储这个任意类型数据的数组
  function getArr3(value: any, count: number): any[] {
    // 根据数据和数量产生一个数组
    const arr: any[] = []
    for (let i = 0; i < count; i++) {
      arr.push(value)
    }
    return arr
  }
  const arr1 = getArr3(100.1233)
  const arr2 = getArr3('abc'3)
  console.log(arr1) //[100.123, 100.123, 100.123] 
  console.log(arr2) //["abc", "abc", "abc"]
  // arr1中存储的是数字类型的数据
  // arr2中存储的是字符串类型的数据
  console.log(arr1[0].toFixed(2)) // 没有任何的智能提示的信息(要么有方法名字的提示信息,要么有错误的提示信息)
  console.log(arr2[0].split('')) // 没有任何的智能提示的信息(要么有方法名字的提示信息,要么有错误的提示信息)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

  function getArr4<T>(value: T, count: number): T[] {
    // 根据数据和数量产生一个数组
    // const arr: T[] = []
    const arr: Array<T> = []
    for (let i = 0; i < count; i++) {
      arr.push(value)
    }
    return arr
  }
  const arr1 = getArr4<number>(200.123455)
  const arr2 = getArr4<string>('abcdefg'5)
  console.log(arr1)
  console.log(arr2)
  console.log(arr1[0].toFixed(3))
  console.log(arr2[0].split(''))
  // // arr1中存储的是数字类型的数据
  // // arr2中存储的是字符串类型的数据
  // console.log(arr1[0].toFixed(2)) // 没有任何的智能提示的信息(要么有方法名字的提示信息,要么有错误的提示信息)
  // console.log(arr2[0].split('')) // 没有

多个参数

1
2
3
4
5
6
7
8
9

// 多个泛型参数的函数:函数中有多个泛型的参数
  function getMsg<KV>(value1: K, value2: V): [KV{
    return [value1, value2]
  }
  const arr1 = getMsg<string,number>('jack',100.2345)
  console.log(arr1[0].split('')) // ["j", "a", "c", "k"] 
  console.log(arr1[1].toFixed(1)) //"100.2"

泛型接口

在定义接口时, 为接口中的属性或方法定义泛型类型,在使用接口时, 再指定具体的泛型类型

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

  // 需求:定义一个类,用来存储用户的相关信息(id,名字,年龄)
  // 通过一个类的实例对象调用add方法可以添加多个用户信息对象,调用getUserId方法可以根据id获取某个指定的用户信息对象
  // 定义一个泛型接口
  interface IBaseCRUD<T> {
    dataArray<T>
    add: (t: T) => T
    getUserId(id: number) => T | undefined
  }
  // 定义一个用户信息的类
  class User {
    id?: number // 用户的id  ? 代表该属性可有可无
    name: string // 用户的姓名
    age: number // 用户的年龄
    // 构造函数
    constructor(name: string, age: number) {
      this.name = name
      this.age = age
    }
  }
  // 定义一个类,可以针对用户的信息对象进行增加及查询的操作
  // CRUD---->create,Read,Update,Delete
  class UserCRUD implements IBaseCRUD<User{
    // 用来保存多个User类型的用户信息对象
    dataArray<User> = []
    //方法用来存储用户信息对象的
    add(user: User): User {
      // 产生id
      user.id = Date.now() + Math.random()
      // 把用户信息对象添加到data数组中
      this.data.push(user)
      return user
    }
    // 方法根据id查询指定的用户信息对象
    getUserId(id:number = 0): User | undefined {
      return this.data.find(user => user.id === id)
    }
  }
  // 实例化添加用户信息对象的类UserCRUD
  const userCRUD: UserCRUD = new UserCRUD()
  // 调用添加数据的方法
  userCRUD.add(new User('jack'20))
  userCRUD.add(new User('tom'25))
  const { id } = userCRUD.add(new User('lucy'23))
  userCRUD.add(new User('rousi'21))
//   [User: {
//   "name": "jack",
//   "age": 20,
//   "id": 1652847195282.5513
// }, User: {
//   "name": "tom",
//   "age": 25,
//   "id": 1652847195282.9338
// }, User: {
//   "name": "lucy",
//   "age": 23,
//   "id": 1652847195282.7893
// }, User: {
//   "name": "rousi",
//   "age": 21,
//   "id": 1652847195282.6506
// }] 
  console.log(userCRUD.data)
  // 根据id查询用户信息对象数据
  const user = userCRUD.getUserId(id)
  console.log(user)
//   User: {
//   "name": "lucy",
//   "age": 23,
//   "id": 1652847195282.7893
// }

泛型类

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

// 定义一个类,类中的属性值的类型是不确定,方法中的参数及返回值的类型也是不确定
// 定义一个泛型类
  class GenericNumber<T>{
    // 默认的属性的值的类型是泛型类型
    defaultValue: T| undefined
    add: ((x: T,y: T) => T)| undefined
  }
  // 在实例化类的对象的时候,再确定泛型的类型
  const g1: GenericNumber<number> = new GenericNumber<number>()
  // 设置属性值
  g1.defaultValue = 100
  // 相加的方法
  g1.add = function (x, y{
    return x + y
  }
  console.log(g1.add(g1.defaultValue,20)) //120 
  // 在实例化类的对象的时候,再确定泛型的类型
  const g2: GenericNumber<string> = new GenericNumber<string>()
  // 设置属性值
  g2.defaultValue = '哈哈'
  // 相加的方法
  g2.add = function (x, y{
    return x + y
  }
  console.log(g2.add('帅杨',g2.defaultValue))  //"帅杨哈哈" 


泛型约束

如果我们直接对一个泛型参数取 length 属性, 会报错, 因为这个泛型根本就不知道它有这个属性

1
2
3
4
5
6
7
8
9
10
11
12

  // 定义一个接口,用来约束将来的某个类型中必须要有length这个属性
  interface ILength{
    // 接口中有一个属性length
    length:number
  }
  function getLength<T extends ILength>(x: T): number {
    return x.length
  }
  console.log(getLength<string>('what are you no sha lei'))
  // console.log(getLength<number>(123))