TypeScript-类、继承、接口、泛型、tsconfig

6/1/2021 TypeScript

# 文章目录

# 一、类

何为类?简而言之就是程序之中所有的操作都需要通过对象来完成。

class Person {
  // 在属性前使用 static 关键字,可以定义静态属性(类属性),不需要创建对象就可以使用 实例对象没有静态属性
  static age: number = 18;
  // 实例属性 readonly表示只读属性,不能修改
  // readonly name: string = "孙悟空";
  name = "孙悟空";

  // 定义方法 加上static 变成静态方法
  sayHello() {
    console.log("大家好");
  }
}

const per = new Person();
console.log("per: ", per);

console.log(Person.age);
// console.log(per.age); 实例对象没有静态属性
console.log(per.name);
// per.name = "齐天大圣";
console.log(per.name);

// 方法
per.sayHello();
// Person.sayHello();
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

# 1.1 构造函数

在实例化一个类的时候,会自动调用该方法。

class Dog {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  bark() {
    console.log("汪汪汪");
  }
}

const dog1 = new Dog("阿喵", 9);
const dog2 = new Dog("阿呆", 6);

console.log(dog1);
console.log(dog2);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 1.2 继承

作用是:共享父类的属性和方法

(function() {
  // 定义公共类
  class Animal {
    name: string;
    age: number;

    constructor(name: string, age: number) {
      this.name = name;
      this.age = age;
    }

    sayHello() {
      console.log("动物在叫");
    }
  }

  // 使用继承后,子类中将拥有父类的所有属性和方法
  class Dog extends Animal {
    run() {
      console.log(`${this.name}在跑~~~`);
    }
    // 子类覆盖父类的方法 => 重写父类方法
    sayHello() {
      console.log("汪汪汪");
    }
  }

  class Cat extends Animal {
    sayHello() {
      console.log("喵喵喵");
    }
  }

  const dog = new Dog("阿呆", 8);
  const cat = new Cat("阿喵", 3);
  console.log("cat: ", cat);
  cat.sayHello();

  console.log("dog: ", dog);
  dog.sayHello();
  dog.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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 1.3 super 关键字

可通过super关键字调用父类的方法。

// super 表示当前类的父类
(function() {
  class Animal {
    name: string;
    constructor(name: string) {
      this.name = name;
    }
    sayHello() {
      console.log("大家好");
    }
  }

  class Dog extends Animal {
    age: number;
    constructor(name: string, age: number) {
      // 如果在子类中写了构造函数,相当于重写了父类的构造函数,此时需要对父类的构造函数进行调用
      super(name); // 相应的参数也要传入进去
      this.age = age;
    }
    sayHello() {
      // super 表示当前类的父类
      super.sayHello();
    }
  }

  const dog = new Dog("旺财", 3);
  dog.sayHello();
})();
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

# 1.4 抽象类与抽象方法

  • 抽象类:即一种专门被其他类继承的类,生下来就是用来给别人继承的(即抽象类是父类),而且在抽象类当中可以添加抽象方法
  • 抽象方法:没有方法体的方法,子类必须实现父类的抽象方法
(function() {
  /**
   * 以 abstract 开头的是抽象类,和其他类区别是:
   *  1.不能被创建实例
   *  2.抽象类可以添加抽象方法
   */
  abstract class Animal {
    name: string;
    constructor(name: string) {
      this.name = name;
    }
    /**
     * abstract 定义一个抽象方法
     *    1.没有方法体
     *    2.抽象方法只能定义在抽象类中
     *    3.子类必须对抽象方法进行重写
     */
    abstract sayHello(): void;
  }

  class Dog extends Animal {
    sayHello() {
      // super 表示当前类的父类
      console.log("汪汪汪~");
    }
  }
  // 非抽象类“Cat”不会实现继承自“Animal”类的抽象成员“sayHello”。
  class Cat extends Animal {
    sayHello() {
      console.log("喵喵喵~");
    }
  }

  const dog = new Dog("旺财");
  const cat = new Cat("阿喵");

  dog.sayHello();

  // const an = 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
35
36
37
38
39
40

# 二、接口

接口就是一组规范

接口与抽象类的区别:

  • 接口:
    1. 都是抽象方法
    2. 属性不能有值,只定义属性类型
    3. interface 定义 implements 实现接口
  • 抽象类:
    1. 可以有抽象方法,也可以有普通方法
    2. class 定义 extends 实现继承
(function() {
  // type 描述一个对象的类型
  type myType = {
    name: string;
    age: number;
  };
  /* 类型声明不可重复声明 */
  // type myType = {
  //   name: string;
  //   age: number;
  // };

  /**
   * 接口:用来定义一个类的结构
   *    1.用来定义一个类中,应该包含那些属性和方法
   *    2.同时也可以当成类型声明去使用
   */
  interface myInterface {
    name: string;
    age: number;
  }
  /* 接口可以重复声明 */
  interface myInterface {
    gender: string;
  }

  const obj: myInterface = {
    name: "sss",
    age: 111,
    gender: "男",
  };

  /**
   *  接口可以在定义类的时候去限制类的值
   *    1.接口中的所有属性都不能有实际的值 属性不能有值,方法不能有方法体
   *    2.接口只定义对象的结构,而不考虑实际的值。
   *    3.接口中所有的方法都是抽象方法
   */

  interface myInter {
    name: string;
    sayHello(): void;
  }
  // 实现接口 => 使类满足接口的要求
  class MyClass implements myInter {
    name: string;
    constructor(name: string) {
      this.name = name;
    }
    sayHello(): void {
      console.log("大家好");
    }
  }
})();
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

# 三、get set 及 public protected private

# 3.1 get And set

如需使用 getter、setter,TS 中已帮我们内置了关键字 get、set 可直接跟上属性名,即可像正常对象的属性一样读取或设置该属性值。

(function() {
  class Person {
    // TS可以在属性前添加修饰符
    /**
     * public 公共属性,可以在任意位置访问或修改 默认值,包括子类和实例
     * protected 受保护的属性,只能在当前类和当前类的子类中使用,不能在实例上访问
     * private 私有属性,只能在类内部访问或修改,不能在子类中访问
     */

    private _name: string;
    private _age: number;
    constructor(name: string, age: number) {
      this._name = name;
      this._age = age;
    }
    // 普通做法
    // getName() {
    //   return this.name;
    // }
    // setName(value: string) {
    //   this.name = value;
    // }
    // getAge() {
    //   return this.age;
    // }
    // setAge(value: number) {
    //   if (value >= 0) {
    //     this.age = value;
    //   } else {
    //     throw new Error("年龄必须大于等于0");
    //   }
    // }

    // TS中做法
    get name(): string {
      return this._name;
    }
    set name(value: string) {
      this._name = value;
    }
    get age(): number {
      return this._age;
    }
    set age(value: number) {
      if (value >= 0) {
        this._age = value;
      } else {
        throw new Error("年龄必须大于等于0");
      }
    }
  }

  const person = new Person("孙悟空", 18);
  // 普通做法
  // console.log(person.getName());
  // person.setName("猪八戒");
  // console.log(person.getName());
  // person.setAge(-5);
  // console.log(person);

  // TS做法
  console.log(person.name);
  person.name = "猪八戒";
  console.log(person.name);
  // person.age = -5;
  console.log(person.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
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

# 3.2 public protected private

class A {
  public name: string;
  protected age: number;
  private num: number;

  constructor(name: string, num: number, age: number) {
    this.name = name;
    this.age = age;
    this.num = num;
  }
}

class B extends A {
  test() {
    console.log(this.name);
    console.log(this.age);
    // console.log(this.num); // 属性“num”为私有属性,只能在类“A”中访问
  }
}
const b = new B("阿喵", 1, 3);
console.log(b.name);
// console.log(b.age); // 属性“age”受保护,只能在类“A”及其子类中访问。
// console.log(b.num); // 属性“num”为私有属性,只能在类“A”中访问。

/**
 * 语法糖,简写了属性的定义及赋值
 */
class C {
  // 可以直接将属性定义在构造函数中
  constructor(public name: string, protected age: number) {}
}

const c = new C("阿呆", 8);
console.log(c);
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

# 四、泛型

在定义函数或者类时,如果遇到类型不明确的,就可以使用泛型。
泛型就是一个不确定的类型。
在我们类型不明确的时候,整一个变量,用这个变量来表示类型(泛型)

/**
 * 在定义函数或者类时,如果遇到类型不明确的,就可以使用泛型
 *    => 泛型就是一个不确定的类型。
 *    泛型的作用就是,在我们类型不明确的时候,整一个变量,用这个变量来表示类型(泛型)
 */

// 这个T是什么类型呢?谁都不知道,只有在函数执行的时候才能确定
// 能体现出传入的参数类型,与返回的参数类型是同一类型
function fn<T>(a: T): T {
  return a;
}

// 直接调用具有泛型的函数
let result = fn(2); // TS会自动推断出参数类型
let result2 = fn<string>("hello"); // 手动指定
console.log("result: ", result);
console.log("result2: ", result2);

function fn2<T, K>(a: T, b: K): T {
  console.log(b);
  return a;
}

fn2<number, string>(123, "hello");

interface Inter {
  length: number;
}

// T extends Inter 表示泛型T必须实现Inter接口,即必须是Inter实现类(子类)
function fn3<T extends Inter>(a: T): number {
  return a.length;
}

fn3("123");
// fn3(456); // 类型“number”的参数不能赋给类型“Inter”的参数。
fn3({ name: "good", length: 5 });
fn3([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>("孙悟空");
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

# 五、tsconfig.js 配置文件解析

{
  // include 包含
  // 一个(*)任意文件,两个(*)任意目录
  "include": ["./src/**/*"],
  // exclude 不包含 默认值:["node_modules","bower_components","jspm_packages"]
  // "exclude": ["./src/hello/**/*"],
  // 定义被继承的配置文件
  // "extends": "",

  // compilerOptions 编译选项
  "compilerOptions": {
    // 打包代码生成的格式
    // 'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021', 'esnext'
    "target": "ES3",
    // 支持的模块化格式 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'esnext'.
    "module": "system",
    // 用来指定项目中要使用的库,为空的话则没有document这些代码提示,以此区分代码提示是在浏览器环境还是Node
    // 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'webworker.iterable', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asyncgenerator', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.object', 'es2019.string', 'es2019.symbol', 'es2020.bigint', 'es2020.promise', 'es2020.sharedmemory', 'es2020.string', 'es2020.symbol.wellknown', 'es2020.intl', 'es2021.promise', 'es2021.string', 'es2021.weakref', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint', 'esnext.string', 'esnext.promise', 'esnext.weakref'.
    // "lib": ['a'],
    // 指定输出文件目录
    "outDir": "./dist",
    // 全局作用域中的代码会合并到同一个文件中
    "outFile": "./dist/main.js",
    // 是否对 JS 文件进行编译:default:false
    "allowJs": true,
    // 检查 JS 代码是否符合 TS语法规范。检查强类型
    "checkJs": true,
    // 是否移除注释
    "removeComments": true,
    // 不生成编译后的文件 default:false
    "noEmit": false,
    // 有错误不生成编译文件 default:false
    "noEmitOnError": true,
    // strict 所有严格检查的总开关 default:false
    "strict": true,
    // 编译后的文件是否使用严格模式。 default:false
    "alwaysStrict": true,
    // 不允许隐式 any 类型 default:false(即默认可以使用 隐式 any)
    "noImplicitAny": false,
    // 不允许不明确类型的 this
    "noImplicitThis": false,
    // 严格的检查空值
    "strictNullChecks": 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
最后更新于: 2021年9月15日星期三晚上10点10分
Dawn
DDRKirby(ISQ)