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 + " \u5403\u98DF\u7269\u3002");
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 + " \u62D2\u7EDD\u5403\u72D7\u7CAE\u3002");
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();

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

时间: 2024-10-29 06:25:03

TypeScript学习笔记(四) - 类和接口的相关文章

初探swift语言的学习笔记四(类对象,函数)

作者:fengsh998 原文地址:http://blog.csdn.net/fengsh998/article/details/29606137 转载请注明出处 假设认为文章对你有所帮助,请通过留言或关注微信公众帐号fengsh998来支持我,谢谢! swift扩展了非常多功能和属性,有些也比較奇P.仅仅有慢慢学习,通过经验慢慢总结了. 以下将初步学习一下类的写法. 码工,最大爱好就是看码,而不是文字,太枯燥. // // computer.swift // swiftDemo // // C

黑马程序员——JAVA学习笔记四(继承、接口、内部类)

1,    通过extends关键字让类与类之间产生继承关系.多个类中存在相同属性和行为时,将这些内容抽取到单独的一个类中,那么多个类无需定义这些属性和行为,只要继承那个类即可,已存在的类叫做超类,基类,或父类.新类称为子类,派生类,孩子类. 子类可以直接访问父类中的非私有的属性和行为.子类无法继承父类中私有的内容.JAVA不支持多继承,只支持单继承,多实现. 继承提高了代码复用性,让类与类之间产生了关系.为多态提供了前提. 2,    super关键字代表父类中成员变量内存空间的标示.两个作用

C++ Primer 学习笔记_23_类与数据抽象(9)--四种对象生存期和作用域、static 用法总结

C++ Primer 学习笔记_23_类与数据抽象(9)--四种对象生存期和作用域.static 用法总结 前言: 从上图可知,程序占用的内存被分了以下几部分. (1).栈区(stack) 存放函数的参数值,局部变量的值等,内存的分配是连续的.栈上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁,其特点是效率高,但空间大小有限 注意:通常栈空间容量比较小,一般是1MB-2MB,所以体积比较大的对象不适合在栈中分配. (2).堆区(heap) 由malloc系列函数或new操作符

Caliburn.Micro学习笔记(四)----IHandle&lt;T&gt;实现多语言功能

Caliburn.Micro学习笔记(四)----IHandle<T>实现多语言功能 说一下IHandle<T>实现多语言功能 因为Caliburn.Micro是基于MvvM的UI与codebehind分离, binding可以是双向的所以我们想动态的实现多语言切换很是方便今天我做一个小demo给大家提供一个思路 先看一下效果 点击英文  变成英文状态点chinese就会变成中文                          源码的下载地址在文章的最下边 多语言用的是资源文件建

C++ Primer 学习笔记_24_类与数据抽象(10)--static 与单例模式、auto_ptr与单例模式、const成员函数、const 对象、mutable修饰符

C++ Primer 学习笔记_24_类与数据抽象(10)--static 与单例模式.auto_ptr与单例模式.const成员函数.const 对象.mutable修饰符 前言 [例]写出面向对象的五个基本原则? 解答:单一职责原则,开放封闭原则,依赖倒置原则,接口隔离原则和里氏替换原则 里氏替换原则:子类型必须能够替换他们的基类型. 设计模式分为三种类型:创建型模式.结构型模式和行为型模式 一.static 与单例模式 1.单例模式 单例模式的意图:保证一个类仅有一个实例,并提供一个访问它

C++ Primer 学习笔记_15_类与数据抽象(1)_类的定义和声明

C++ Primer 学习笔记_15_类与数据抽象(1)_类的定义和声明 在C++中,用类来定义自己的抽象数据类型.通过定义类型来对应所要解决的问题中的各种概念,可以使我们更容易编写.调试和修改程序.可以使得自己定义的数据类型用起来与内置类型一样容易和直观. 看一下Sales_item类: class Sales_item { private: std::string isbn; unsigned units_sold; double revenue; public: double ave_pr

Swift学习笔记(11)--类与结构体

类与结构是编程人员在代码中会经常用到的代码块.在类与结构中可以像定义常量,变量和函数一样,定义相关的属性和方法以此来实现各种功能. 和其它的编程语言不太相同的是,Swift不需要单独创建接口或者实现文件来使用类或者结构.Swift中的类或者结构可以在单文件中直接定义,一旦定义完成后,就能够被直接其它代码使用. 注意:一个类的实例一般被视作一个对象,但是在Swift中,类与结构更像是一个函数方法,在后续的章节中更多地是讲述类和结构的功能性. 1.类和结构的异同 类和结构有一些相似的地方,它们都可以

Boost Thread学习笔记四

barrierbarrier类的接口定义如下: 1 class barrier : private boost::noncopyable   // Exposition only 2 { 3 public: 4   // construct/copy/destruct 5   barrier(size_t n); 6   ~barrier(); 7  8   // waiting 9   bool wait();10 }; barrier类为我们提供了这样一种控制线程同步的机制:前n - 1次调

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员)、拷贝构造函数

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员).拷贝构造函数  从概念上将,可以认为构造函数分为两个阶段执行: 1)初始化阶段: 2)普通的计算阶段.计算阶段由构造函数函数体中的所有语句组成. 一.构造函数初始化列表 推荐在构造函数初始化列表中进行初始化 1.对象成员及其初始化 <span style="font-size:14px;">#include <iostream> using namespace std;

Swift学习笔记:类和结构

一.类和结构的异同 类和结构有一些相似的地方,它们都可以: 1. 定义一些可以赋值的属性: 2. 定义具有功能性的方法 3. 定义下标,使用下标语法 4. 定义初始化方法来设置初始状态 5. 在原实现方法上的可扩展性 根据协议提供某一特定类别的基本功能 1. 类还有一些结构不具备的特性: 2. 类的继承性 3. 对类实例实时的类型转换 4. 析构一个类的实例使之释放空间 5. 引用计数,一个类实例可以有多个引用 1. 定义语法 struct Name{ let firstName = "&quo