• TypeScript学习笔记(四)


    本篇将介绍TypeScript里的类和接口。

    与其他强类型语言类似,TypeScript遵循ECMAScript 2015标准,支持class类型,同时也增加支持interface类型。

    一、类(class)

    下面是一个类的基本定义方式:

     1 class User {
     2     name: string;
     3     constructor(_name: string) {
     4         this.name = _name;
     5     }
     6 
     7     sayHello(): string {
     8         return `Hello,${this.name}!`;
     9     }
    10 }
    11 
    12 let user = new User('John Reese');
    13 user.sayHello();

    在上面的例子里,定义了一个类User,这个类拥有一个属性、一个构造函数和一个实例方法sayHello。通过new的方式,可以用这个类实例化一个实例对象,并可以调用实例方法。这与大多数静态语言的声明方式一致。

    1. 类成员的访问级别

    与强类型语言类似,TypeScript的类成员可以显式声明访问级别:public、protected、private

     1 class User {
     2     name: string;
     3     private sex: string;
     4     protected age: number;
     5     constructor(_name: string) {
     6         this.name = _name;
     7     }
     8 
     9     sayHello(): string {
    10         return `Hello,${this.name}!`;
    11     }
    12 }
    13 
    14 let user = new User('John Reese');
    15 user.name = 'Root';                 // 公有属性,可以赋值
    16 user.sex = 'female';                // 私有属性,无法赋值
    17 user.age = 28;                      // 受保护属性,无法赋值
    18 user.sayHello();

    在TypeScript里,如果不显示指定访问级别,则默认为public。

    2. 属性的get和set访问器

     1 class User {
     2     private _name: string;
     3     
     4     get name(): string {
     5         return this._name;
     6     }
     7 
     8     set name(newName: string) {
     9         this._name = newName;
    10     }
    11 
    12     constructor(_name: string) {
    13         this.name = _name;
    14     }
    15 
    16     sayHello(): string {
    17         return `Hello,${this._name}!`;
    18     }
    19 }
    20 
    21 let user = new User('John Reese');
    22 user.name = 'Root';                 
    23 user.sayHello();

    通过get和set关键字声明属性访问器,通过属性访问器可以精确控制属性的赋值和获取值。下面是经过编译后生成的JavaScript代码

     1 var User = (function () {
     2     function User(_name) {
     3         this.name = _name;
     4     }
     5     Object.defineProperty(User.prototype, "name", {
     6         get: function () {
     7             return this._name;
     8         },
     9         set: function (newName) {
    10             this._name = newName;
    11         },
    12         enumerable: true,
    13         configurable: true
    14     });
    15     User.prototype.sayHello = function () {
    16         return "Hello," + this._name + "! " + this._age;
    17     };
    18     return User;
    19 }());
    20 var user = new User('John Reese');
    21 user.name = 'Root';
    22 user.sayHello();

    3. 静态属性

    静态属性即是通过类型而不是实例就可以访问的属性

     1 class User {
     2     static sex_type = ['male', 'female'];       // 静态属性
     3     name: string;
     4     sex: string;
     5 
     6     constructor(_name: string) {
     7         this.name = _name;
     8     }
     9 
    10     sayHello(): string {
    11         return `Hello,${this.name}!`;
    12     }
    13 }
    14 
    15 let user = new User('John Reese');
    16 user.name = 'Root';
    17 user.sex = User.sex_type[1];
    18 user.sayHello();

    通过static关键字可以声明类型的静态属性。

    4. 类的继承

    同强类型语言一样,TypeScript也支持类的继承

     1 // 基类
     2 class Animal {
     3     name: string;
     4 
     5     constructor(theName: string) {
     6         this.name = theName;
     7     }
     8 
     9     eat() {
    10         console.log(`${this.name} 吃食物。`);
    11     }
    12 }
    13 
    14 // 子类继承基类
    15 class Dog extends Animal {
    16     constructor(theName: string) {
    17         super(theName);
    18     }
    19 
    20     eat() {
    21         super.eat();
    22         console.log('并且吃的是狗粮。');
    23     }
    24 }
    25 
    26 class People extends Animal {
    27     constructor(theName: string) {
    28         super(theName);
    29     }
    30 
    31     // 子类重写基类方法
    32     eat() {
    33         console.log(`${this.name} 拒绝吃狗粮。`);
    34     }
    35 }
    36 
    37 let animal = new Animal('动物');
    38 animal.eat();
    39 
    40 let dog: Animal;
    41 dog = new Dog('狗');
    42 dog.eat();
    43 
    44 let people: Animal;
    45 people = new People('人类');
    46 people.eat();

    从上面的例子可以看到,子类通过extends关键字可以继承其他类,通过super方法调用基类对应的方法,也可以直接重写基类的方法。

    下面是编译之后生成JavaScript源码,可以比较看看

     1 var __extends = (this && this.__extends) || function (d, b) {
     2     for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
     3     function __() { this.constructor = d; }
     4     d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
     5 };
     6 // 基类
     7 var Animal = (function () {
     8     function Animal(theName) {
     9         this.name = theName;
    10     }
    11     Animal.prototype.eat = function () {
    12         console.log(this.name + " u5403u98DFu7269u3002");
    13     };
    14     return Animal;
    15 }());
    16 // 子类继承基类
    17 var Dog = (function (_super) {
    18     __extends(Dog, _super);
    19     function Dog(theName) {
    20         _super.call(this, theName);
    21     }
    22     Dog.prototype.eat = function () {
    23         _super.prototype.eat.call(this);
    24         console.log('并且吃的是狗粮。');
    25     };
    26     return Dog;
    27 }(Animal));
    28 var People = (function (_super) {
    29     __extends(People, _super);
    30     function People(theName) {
    31         _super.call(this, theName);
    32     }
    33     // 子类重写基类方法
    34     People.prototype.eat = function () {
    35         console.log(this.name + " u62D2u7EDDu5403u72D7u7CAEu3002");
    36     };
    37     return People;
    38 }(Animal));
    39 var animal = new Animal('动物');
    40 animal.eat();
    41 var dog;
    42 dog = new Dog('狗');
    43 dog.eat();
    44 var people;
    45 people = new People('人类');
    46 people.eat();
    编译之后的JavaScript源码

    5. 抽象类

    将上面的例子稍微修改下

     1 // 抽象类
     2 abstract class Animal {
     3     name: string;
     4 
     5     constructor(theName: string) {
     6         this.name = theName;
     7     }
     8 
     9     abstract eat();
    10 }
    11 
    12 // 子类继承抽象类
    13 class Dog extends Animal {
    14     constructor(theName: string) {
    15         super(theName);
    16     }
    17 
    18     eat() {
    19         console.log(`${this.name} 吃狗粮。`);
    20     }
    21 }
    22 
    23 let animal = new Animal('动物');      // 抽象类无法实例化
    24 animal.eat();
    25 
    26 let dog: Animal;
    27 dog = new Dog('狗');
    28 dog.eat();

    通过abstract关键字声明抽象类和抽象方法,子类继承抽象类后,需要实现抽象方法。同样的,抽象类不能被实例化。

     

    二、接口

    下面是一个简单的接口声明

    1 interface Animal {
    2     name: string;
    3 }

    在JavaScript里没有对应的类型与之对应,所以编译之后不会生成任何JavaScript代码。

    1. 作为参数类型

    接口类型可以作为方法的参数类型,效果等同于直接指定Json对象的类型。

    1 interface Animal {
    2     name: string;
    3 }
    4 
    5 let printName = function(param: Animal) {
    6     console.log(`Name is ${param.name}`);
    7 }
    8 
    9 printName({name: 'Dog'});

    同样,接口成员也可以是缺省的

     1 interface Animal {
     2     name: string;
     3     age?: number;
     4 }
     5 
     6 let printName = function (param: Animal) {
     7     if (param.age) {
     8         console.log(`Name is ${param.name}, and age is ${param.age}`);
     9     } else {
    10         console.log(`Name is ${param.name}`);
    11     }
    12 }
    13 
    14 printName({ name: 'Dog' });
    15 printName({ name: 'Dog', age: 5 });

    但是在某些情况下,调用方法时,参数赋值可能会有多个,接口在作为参数类型时也支持拥有多个成员的情况。

     1 interface Animal {
     2     name: string;
     3     age?: number;
     4     [propName: string]: any;        // 其他成员
     5 }
     6 
     7 let printName = function (param: Animal) {
     8     if (param.age) {
     9         console.log(`Name is ${param.name}, and age is ${param.age}`);
    10     } else {
    11         console.log(`Name is ${param.name}`);
    12     }
    13 }
    14 
    15 printName({ name: 'Dog' });
    16 printName({ name: 'Dog', age: 5 });
    17 printName({ name: 'Dog', age: 5, character: '粘人' });    // 多于明确定义的属性个数

    2. 作为其他类型

    接口也可以定义方法的类型,和数组类型

     1 interface FuncType {
     2     (x: string, y: string): string;         // 声明方法成员
     3 }
     4 
     5 let func1: FuncType;
     6 func1 = function (prop1: string, prop2: string): string {       // 方法参数名称不需要与接口成员的参数名称保持一致
     7     return prop1 + ' ' + prop2;
     8 }
     9 
    10 interface ArrayType {
    11     [index: number]: string;                // 声明数组成员
    12 }
    13 
    14 let arr: ArrayType;
    15 arr = ['Dog', 'Cat'];

    3. 接口的继承与实现

    同强类型语言一样,TypeScript的接口支持继承与实现。

     1 interface Animal {
     2     name: string;
     3     eat(): void;
     4 }
     5 
     6 class Dog implements Animal {
     7     name: string;
     8     constructor(theName: string) {
     9         this.name = theName;
    10     }
    11 
    12     eat() {
    13         console.log(`${this.name} 吃狗粮。`)
    14     }
    15 }
    16 
    17 class Cat implements Animal {
    18     name: string;
    19     constructor(theName: string) {
    20         this.name = theName;
    21     }
    22 
    23     eat() {
    24         console.log(`${this.name} 吃猫粮。`)
    25     }
    26 }
    27 
    28 let dog: Animal;
    29 dog = new Dog('狗狗');
    30 dog.eat();
    31 
    32 let cat: Animal;
    33 cat = new Cat('喵星人');
    34 cat.eat();

    类通过implements关键字继承接口,并实现接口成员。

    同时,接口也可以多重继承。

     1 interface Animal {
     2     name: string;
     3     eat(): void;
     4 }
     5 
     6 interface Person extends Animal {                   // 继承自Animal接口
     7     use(): void;
     8 }
     9 
    10 class People implements Person {
    11     name: string;
    12     constructor(theName: string) {
    13         this.name = theName;
    14     }
    15 
    16     eat() {
    17         console.log(`${this.name} 拒绝吃狗粮。`)
    18     }
    19 
    20     use() {
    21         console.log(`${this.name} 会使用工具。`)
    22     }
    23 }
    24 
    25 let man: Person;
    26 man = new People('男人');
    27 man.eat();
    28 man.use();

    4. 类型转换

    在TypeScript里,接口可以对符合任一成员类型的对象进行转换,转换之后的对象自动继承了接口的其他成员。

     1 interface Animal {
     2     name: string;
     3     age: number;
     4     eat(): void;
     5 }
     6 
     7 let thing = { name: '桌子' };
     8 let otherThing = <Animal>thing;             // 类型转换
     9 otherThing.age = 5;
    10 otherThing.eat = function () {
    11     console.log(`${this.name} 不知道吃什么。`)
    12 };

    上面的例子里,声明了拥有name属性的json对象,通过<>将json对象转换成了Animal类型的对象。转换后的对象则拥有了另外的age属性和eat方法。

    5. 接口继承类

    在TypeScript里,接口可以继承类,这样接口就具有了类里的所有成员,同时这个接口只能引用这个类或者它的子类的实例。

     1 class People {
     2     name: string;
     3     private age: number;
     4     constructor(theName: string) {
     5         this.name = theName;
     6     }
     7 
     8     eat() {
     9         console.log(`${this.name} 拒绝吃狗粮。`);
    10     }
    11 
    12     use() {
    13         console.log(`${this.name} 会使用工具。`)
    14     }
    15 }
    16 
    17 interface Animal extends People {               // 接口
    18 
    19 }
    20 
    21 class Man extends People {                      // 子类
    22 
    23 }
    24 
    25 class Cat {                                     // 拥有同样结构的另外一个类
    26     name: string;
    27     private age: number;
    28     constructor(theName: string) {
    29         this.name = theName;
    30     }
    31 
    32     eat() {
    33         // 具体实现
    34     }
    35 
    36     use() {
    37         // 具体实现
    38     }
    39 }
    40 
    41 let cat: Animal;
    42 cat = new Cat('喵星人');                       // Cat类不是People的子类,无法被Animal引用
    43 
    44 let man: Animal;
    45 man = new Man('男人');
    46 man.eat();

    当继承链过深,代码需要在某一个子类的类型下执行时,这种方法比较有效。

  • 相关阅读:
    Rancher 中 Traefik 负载均衡 Initializing 状态
    【音视频】YUV、RGB视频像素处理
    Debian WSL 2 安装使用 Docker
    CentOS 7 切换 Java 版本到 Java 11
    阿里云 CentOS 8.2 停服后 yum / dnf 无法安装更新
    CentOS 8 Stream 报错处理 Faild to start Load Kernel Modules. Failed to insert 'ipmi_si': No such device
    System.getProperty()获取系统变量
    简单科普私钥、地址、助记词、Keystore的区别
    synchronized实现原理及锁升级过程
    H3C 策略路由原理介绍
  • 原文地址:https://www.cnblogs.com/niklai/p/5778341.html
Copyright © 2020-2023  润新知