TS

一:认识ts

为什么要学习ts

  1. TypeScript 是由微软开发的

2.一款开源的编程语言。

  1. TypeScript 是Javascript的超级,遵循最新的ES6、 Es5规范。 TypeScript扩展了JavaScript的语法。

  2. TypeScript 更像后端java、 C#这样的面向对象语言可以让js开发大型企业项目。

5.谷歌也在大力支持Typescript 的推广,谷歌的angular2. x+就是基于Typescript语法。最新的Vue3.0、React 也可以集成TypeScript.

TypeSc ript的设计目的应该是解决JavaScript的“痛点” :

1.弱类型和没有命名空间,导致很难模块化,

2.不适合开发大型程序。

3.提供了一些语法糖来帮助大家更方便地实践面向对象的编程。(类, 接口,枚举,泛型,方法重载等)

  1. TypeScript的一个设计亮点就是它并没有抛弃JavaScript的语法另起炉灶,而是做成了JavaScript的超集(这个功劳应该记在Anders上),这样任何合法的JavaScript的语句在TypeScript下都是合法的,也就是说学习成本很低

ts的编译安装

  1. npm install -g typescript
  2. tsc 指向ts结尾的文件

借助vscode编辑器上面的Terminal

  1. tsc –init
  2. Run Task

3.选中typescript
4. tsc:watch

二:ts的原始数据类型

JavaScript的类型分为两种:原始数据类型和对象类型
原始数据类型包括:布尔值、数值、字符串、 nullundefined以及ES6中的新类型[‘Symbol ]和[ BigInt ]
本文主要介绍 前五种 原始数据类型在TypeScript中的应用。

1:布尔值

布尔值是最基础的数据类型,在TypeScript中,使用boolean定义布尔值类型:

Let isDone: boolean = false;
//编译通过
//后面约定,未强调编译错误的代码片段,默认为编译通过

注意,使用构造函数Boolean创造的对象**不是** 布尔值:
let createdByNewBoolean: boolean = new BooLean(1);

//new BooLean()返回的是一个BooLean对象
let createdByNewBoolean: Boolean = new BooLean(1);

//直接调用boolean,返回一个boolean类型
createdByNewBoolean boolean= BooLean(1);

//报错,不允许改变类型,
createdByNewBoolean='1,2,3'
复制代码

在TypeScript 中,boolean是JavaScript中的基本类型,而Boolean是JavaScript中的构造函数。其他基本类型(除了nullundefined)一样,不再赘述。

2:数值

使用number定义数值类型:

Let decliteral: number = 6;
复制代码

编译结果:

var decLiteral = 6;
复制代码

3:字符串

使用string 定义字符串类型:

let myName: string = 'Tom';
let myAge: number = 25;
//模板字符串
let sentence: string = `Hello, my name is ${myName}I'll be ${myAge + 1} years old next month.`
复制代码

编译结果:

var myName = 'Tom';
var myAge = 25;
//模板字符串
var sentence = "Hello, my name is" + myName + "I'll be"+ (myAge + 1) +"years old next month.";
复制代码

其中 … 用来定义[ES6 中的模板字符串] ${expr}用来在模板宇符串中嵌入表达式。

4:空值

JavaScript没有空值-(Void)的概念,在TypeScript中,可以用void表示没有任何返回值的函数:

function alertName(): void {
alert('My name is Tom');
}
复制代码

声明一个void 类型的变量没有什么用,因为你只能将它赋值为undefinednull:

let unusable: void = undefined;
复制代码

5: Null 和Undefined

在TypeScript 中,可以使用nullundefined来定义这两个原始数据类型:

let u: undefined = undefined ;
let n: null = null;
复制代码

三:任意值

任意值(Any) 用来表示允许赋值为任意类型。

什么是任意值类型

如果是一个普通类型,在赋值过程中改变类型是不被允许的:

let myFavoriteNumber: string = 'seven';
myFavoriteNumber =7;
// index.ts(2,1) 报错: error TS2322: Type 'number' is not assignable to type'string'. 
复制代码

但如果是any 类型,则允许被赋值为任意类型。

let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;
复制代码

任意值的属性和方法

在任意值上访问任何属性都是允许的:

let anyThing:any ='hello';
console. log( anyThing.myName);
console. log(anyThing.myName.firstName);
复制代码

也允许调用任何方法:

let anyThing: any = 'Tom' ;
anyThing. setName( 'Jerry');
anyThing. setName( 'Jerry' ). sayHello();
anyThing. myName. setFirstName('Cat');
复制代码

可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值.

未声明类型的变量

变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型:

let something; 
something = 'seven';
something = 7;
something . setName('Tom'); 
复制代码

等价于

let something: any;
something = 'seven':
something = 7;
something. setName('Tom');
复制代码

四:类型推论

如果没有明确的指定类型,那么TypeScript 会依照类型推论(Type Inference) 的规则推断出一个类型。

什么是类型推论

以下代码虽然没有指定类型,但是会在编译的时候报错:

let myFavoriteNumber = 'seven'; 
myFavoriteNumber = 7;
// index.ts(2,1) 报错: error TS2322: Type 'number' is not assignable to type'string'.
复制代码

事实上,它等价于:

let myFavoriteNumber: string = 'seven' ; 
myFavoriteNumber = 7;
// index.ts(2,1) 报错: error TS2322: Type " number' is not assignable to type 'string'.
复制代码

TypeScript会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成any 类型而完全不被类型检查:

五:联合类型

联合类型(Union Types)表示取值可以为多种类型中的种。用或链接|

简单的例子

let myFavoriteNumber: string | number ;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
let myFavoriteNumber: string I number;
myFavoriteNumber = true;
// index.ts(2,1): error TS2322: Type ' boolean' is not assignable to type'stringI number'.
//Type 'boolean' is not assignable to type 'number '.
复制代码

联合类型使用|分隔每个类型。

这里的let myFavoriteNumber: string | number 的含义是, 允许myFavoriteNumber 的类型是 stringnumber,但是不能是其他类型。

访问联合类型的属性或方法

当TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法:

function getLength (something: string | number): number {
return something.Length;}
// index.ts(2,22): error TS2339: Property 'length' does not exist on type string| number' .
// Property 'length' does not exist on type 'number'. 

复制代码

上例中,length不是string number的共有属性, 所以会报错。
访问string number的共有属性是没问题的:

function getSt ring( something: stringI number): string {return something. toString();}
复制代码

六:对象的类型—接口

在TypeScript中,我们使用接口Interfaces 来定义对象的类型。

什么是接口

在面向对象语言中,接口Interfaces 是个很重要的概念,它是对行为的抽象,而具体如何行动需要由类classes 去实现implement.

TypeScript中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外, 也常用于对对象的形状Shape 进行描述

简单的例子

interface Person {
name: string;
age: number;
//let的必须和上面格式一样
let tom: Person = {
name: 'Tom' ,
age: 25
};
复制代码

上面的例子中,我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是Person. 这样,我们就约束了tom 的形状必须和接口Person 一致。

接口一半字母大写,有的编程语言会建议接口的名称加上I前缀

定义的变量比接口少了一些属性的行为是不允许的:

interface Person {
name: string;
age: number;
//let的必须和上面格式一样
let tom: Person = {
name: 'Tom' ,
//age: 25//报错,少了不行


interface Person {
name: string;
age: number;
//let的必须和上面格式一样
let tom: Person = {
name: 'Tom' ,
age: 25,
color:blue//报错。多了也不行
复制代码

可见,赋值的时候,变量的形状必须和接口的形状保持一致

可选属性

有时我们希望不要完全匹配一个形状,那么可以用可选属性: 用?

interface Person 
name: string;
age?: number;
let tom: Person = {
name: 'Tom '
};
interface Person {
name: string;
age?: number;
};
let tom: Person = {
name: 'Tom'
age: 25
};
复制代码

可选属性的含义是该属性可以不存在。
这时仍然不允许添加未定义的属性:

interface Person {
name: string;
age?: number;
}
Let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male '
}
//报错: examples/p layground/ index.ts(9,5): error TS2322: Type '{ name: string;age: number; gender: string; }' is not assignable to type ' Person 'Object literal may only specify known properties, and ' gender' does not exist in type 'Person'.
复制代码

任意属性

有时候我们希望一个接口允许有任意的属性,可以使用如下方式:

interface Person {
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = E
name: 'Tom' ,
Q
gender:,'male'
};
复制代码

使用[propName: string] 定义了任意属性取string 类型的值。
需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:任意类型[propName: string]: string;

interface Person {
name: string;
age?: number;
//任意类型,一般都会写any
[propName: string]: any;
}
let tom: Person = {
name: 'Tom' ,
age: 25 ,
gender:'male'
};
复制代码

只读属性

有时候我们希望对象中的一些字段只能在创建的时候被赋值, 那么可以用readonly定义只读属性:

interface Person {
readonly id: number ;
name: string;
age?: number ; 
[propName: string]: any;
}
//只读属性用readonly声明,给值是在声明变量时候给值,
let tom: Person = {
id: 89757,
name: ' Tom' ,
gender: ' male '
};
tom.id = 9527;
// 报错:index. ts(14,5): error TS2540: Cannot assign to ' id' because it is a constant or a read-only property. 
复制代码

上例中,使用readonly定义的属性id 初始化后, 又被赋值了,所以报错了。
注意,只读的约束存在于第一次给对象赋值的时候, 而不是第一次给只读属性赋值的时候:

interface Person {
readonly id: number ;
name: string;
age?: number ; 
[propName: string]: any;
}
//只读属性用readonly声明,给值是在声明变量时候给值,
let tom: Person = {
name: ' Tom' ,
gender: ' male '
};
tom.id = 9527;
复制代码

上例中,报错信息两处:

1:是在对tom进行赋值的时候没给id赋值。

2:在给tom.id赋值的时候,由于它是只读属性,所以报错了。

注意,只读的约束存在于第一次给对象赋值的时候, 而不是第一次给只读属性赋值的时候;

实战应用:

interface Config {
    type: string;
    url: string;
    data ? : string;
    dataType: string;
}

function ajax(config:Config) {
    var xhr = new XMLHttpRequet();
    xhr.open(config.type, config.url, true);
    xhr.send(config.data);
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4 && xhr.status == 200) {
            console.Log('success')
        }
    }
}
ajax({
    type: 'get',
    url: 'https:/ /www. baidu.com',
    dataType: 'json'
})

复制代码

七:数组类型

「类型 +方括号」表示法

最简单的方法是使用「类型+方括号」来表示数组:

let fibonacci: number[] = [1, 1,2, 3, 5];
复制代码

数组的项中不允许出现其他的类型:

let fibonacci: number[] = [1, '1', 2, 3, 5];
// Type 'string' is not assignable to type 'number'. 
复制代码

数组的一些方法的参数也会根据数组在定义时约定的类型进行限制:

let fibonacci: number[] = [1, 1, 2, 3, 5];
fibonacci. push('8');
// Argument of type ""1"” is not assignable to parameter of type " number' 
复制代码

上例中,push方法只允许传入number类型的参数,但是却传了一个"8"类型的参数,
所以报错了。这里"8" 是一个字符串字面量类型,会在后续章节中详细介绍。

数组泛型

我们也可以使用数组泛型(Array Generic)、Array<elemType> 来表示数组:

let fibonacci: Array<number> = [1, 1, 2, 3, 5];
复制代码

用接口表示数组

接口也可以用来描述数组:

inter face NumberArray {
[index: number]: number;
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
复制代码

NumberArray表示:只要索引的类型是数字时,那么值的类型必须是数字。

虽然接口也可以用来描述数组,但是我们一般不会这么做,因为这种方式比前两种方式复杂多了。不过有一种情况例外,那就是它常用来表示类数组。

类数组

类数组(Array-like object) 没有数组的方法,有length属性。不是数组类型,比如arguments :

function sum() C
let args: number[] = arguments;
// Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.
复制代码

上例中,arguments 实际上是一个类数组,不能用普通的数组的方式来描述,而应该用接口:

function sum() {
let args: {
[index: number]: number;
length: number;
callee: Function;
} = arguments;
}
复制代码

在这个例子中,我们除了约束当索引的类型是数字时,值的类型必须是数字之外,也约束了它还有lengthcallee两个属性。

事实上常用的类数组都有自己的接口定义,如 IArgymentsNodeList,HTMLCollection等:

function sum() 
let args: IArguments = arguments;
}
复制代码

其中 IArguments是TypeScript 中定义好了的类型,它实际上就是:

interface IArguments {
[index: number]: any;
Length: number;
callee: Function ;
}
复制代码

any在数组中应用

一个比较常见的做法,用any表示数组中允许出现任意类型:

let list:any[]=['sb',25{website:'http://sb.com'}]
复制代码

八:函数的类型

函数是JavaScript 中的一 等公民

函数声明

在JavaScript中,有两种常见的定义函数的方式一函数声明 (Function Dec La ration)和函数表达式(Function Expression)

//函数声明(Function Declaration)
function sum(x, y) {
return x + y:
//函数表达式(Function Expression)
let mySum = function (x, y) {
return x + y;
};
复制代码

一个函数有输入和输出,要在TypeScript中对其进行约束,需要把输入和输出都考虑到,其中函数声明的类型定义较简单:

function sum(x: number, y: number): number {
return x + y;
}
复制代码

注意,输入多余的(或者少于要求的)参数,是不被允许的


function sum(x: number, y: number): number {
return x + y;
}
sum(1, 2, 3);

// index.ts(4,1): error TS2346: Supplied parameters do not match anysignature of call target .

function sum(x: number, y: number): number {
return x + y:
} 
sum(1);
// index.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target 
复制代码

函数表达式

如果要我们现在写一个对函数表达式(Function Expression) 的定义,可能会写成这样:

Let: mySum = function (x:,number,,y:,number):,number{
return X:+ y:
};
复制代码

这是可以通过编译的,不过事实上,上面的代码只对等号右侧的匿名函数进行了类型定义,而等号左边的mySum 是通过赋值操作进行类型推论而推断出来的。如果需要我们手动给mySum、添加类型,则应该是这样:

let mySum: (x: number, y: number) => number = function (x: number, y:number): nunmber {
return X + y
};
复制代码

注意不要混淆了TypeScript 中的=>和ES6中的 =>
在TypeScript 的类型定义中=>用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。

在ES6中,叫做箭头函数,应用十分广泛,可以参考ES6 中的箭头函数

用接口定义函数的形状

我们也可以使用接口的方式来定义个函数需要符合的形状:

interface SearchFunc {
(source: string, subString: string): booLean;
let mySearch: SearchFunc;
mySearch = funct ion(source: string ,
subString: string) { 
return source.search( subString) !== -1;
}
复制代码

采用函数表达式|接口定义函数的方式时,对等号左侧进行类型限制,可以保证以后对函数名赋值时保证参数个数参数类型返回值类型不变。

可选参数

前面提到,输入多余的(或者少于要求的)参数,是不允许的。那么如何定义可选的参数呢?
与接口中的可选属性类似,我们用?表示可选的参数:

function bui LdName(f irstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName ;
} else {
return firstName ;
}
}
Let tomcat = buildName('Tom', 'Cat');
let tom = buildName( 'Tom' );
复制代码

需要注意的是,可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必需参数了:

function buildName (firstName?: string, lastName: string) {
if (firstName) {
return firstName + ' ' + las tName;
} else {
return Las tName ;
}
}
let tomcat = buildName('Tom', 'Cat');
Let tom= buildName(undefined, 'Tom' );
// index. ts(1,40): error TS1016: A required parameter cannot follow an optional parameter .
复制代码

参数默认值

在ES6中,我们允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数 :

function bui LdName( firstName: string, lastName: string = 'Cat') {
return firstName + ' ' + lastName; 
let tomcat = buildName('Tom' , 'Cat');
let tom = buildName( 'Tom');
复制代码

此时就不受「可选参数必须接在必需参数后面」的限制了:

function bui LdName (firstName: string = 'Tom', lastName: string) {
return firstName + ' ' + lastName; 
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName (undefined, 'Cat' );
复制代码

关于默认参数,可以参考ES6中函数参数的默认值

剩余参数

ES6中,可以使用...rest 的方式获取函数中的剩余参数(rest 参数) :

function push(array, ... items) {
items. forEach( function(item) {
ar ray . push( item) ;
});
}
Let a: any[] = [];
push(a, 1, 2, 3);
复制代码

事实上,items是一个数组。所以我们可以用数组的类型来定义它:

function push(array: any[], ...items: any[]) {
items . forEach( function(item) {
array. push( item);
});
}
leta=[];
push(a, 1, 2, 3);
复制代码

注意,rest 参数只能是最后一个参数,关于rest 参数,可以参考ES6 中的rest 参数

重载

重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。

比如,我们需要实现一个函数reverse,输入数字123的时候, 输出反转的数字321, 输入字符串hello 的时候,输出反转的字符串olleh .

利用联合类型,我们可以这么实现:

function reverse(x: number | string): number | string {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}
复制代码

然而这样有一个缺点, 就是不能够精确的表达,输入为数字的时候,输出也应该为数字,输入为字符串的时候,输出也应该为字符串。
这时,我们可以使用重载定义多个reverse 的函数类型:

function reverse(x: number): number;

function reverse(x: string): string;

function reverse(x: number | string): number | string {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}
复制代码

上例中,我们重复定义了多次函数reverse,前几次都是函数定义,最后次是函数实现。在编辑器的代码提示中,可以正确的看到前两个提示。

注意,TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。

比如说现在需要写个函数,符合此函数规则的只有三个人: 姚明,科比和詹姆斯; 只有当名字是姚明的时候,他才可以打中锋,是科比的时候才能打后卫,是詹姆斯的时候才能打前锋,并且因为国内球员比较瘦弱,所以只有当姚明是25岁以上的时候,才能被归为合格的中锋(只是举个例子,不要太当真),那这个时候就用到特定重载签名了。

function playBasketball(name: 'YaoMing', age: number): void;

function pLayBasketbalL(name: 'Kobe ', age ? : number): void

function playBasketball(name: 'James', age ? : number): void;

function playBasketball(name: string, age ? : number) {
    if (name ===
        'YaoMing' && age && age >= 25) {
        console.log('good Center')
    } else if (name === 'Kobe') {
        console.Log('good guard')
    } else if (name === 'Jams') {
        console.Log('good forward')
    } else {
        console.log('ordinary baskerball player')
    }
}
playBasketball('YaoMing', 25);
playBasketball('Kobe');
复制代码

九:枚举

枚举(Enum) 类型用于取值被限定在一定范围内的场景,比如一周只能有七天,颜色限定为红绿蓝等。

简单的例子

枚举使用enum 关键字来定义:

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
复制代码

枚举成员会被赋值为从、 0开始递增的数字,同时也会对枚举值到枚举名进行反向映射:

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console. log(Days["Sun"] === 0); // true
console. Log(Days["Mon"] === 1); // true
console. Log(Days["Tue"] === 2); // true
console. log(Days["Sat"] === 6); // true
console. Log(Days[0] === "Sun"); // true
console. log(Days[1] === "Mon"); // true
console. log(Days[2] === "Tue"); // true
console. log(Days[6] === "Sat"); // true
复制代码

手动赋值

我们也可以给枚举项手动赋值:

enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};

console. log(Days["Sun"] === 7); // true
console. log(Days ["Mon"] === 1); // true 
console. log(Days["Tue"] === 2); // true
console. Log(Days["Sat"] === 6); // true 
复制代码

上面的例子中,未手动赋值的枚举项会接着上一个枚举项递增。

如果未手动赋值的枚举项与手动赋值的重复了,TypeScript 是不会察觉到这一点的:

enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};
console. log (Days["Sun"] === 3); // true
console. Log(Days ["Wed"] === 3); // true
console. log(Days[3] === "Sun"); // false
console. Log(Days[3] === "Wed"); // true 
复制代码

十:TypeScript中类的用法

public private 和 protected(公共的,私有的,受保护的)

TypeScript可以使用三种访问修饰符(Access Modifiers) ,分别是public ,privateprotected

public 修饰的属性或方法是公有的, 可以在任何地方被访问到, 默认所有的属性和方法都是public

private 修饰的属性或方法是私有的, 不能在声明它的类的外部访问
protected 修饰的属性或方法是受保护的, 它和private类似, 区别是它在子类中也是允许被访问的
下面举一些例子:

class Animal {
    public name;
    public constructor(name) {
        this.name = name;
    }
}
let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';
console.log(a.name); // Tom
复制代码

.上面的例子中,name 被设置为了public, 所以直接访问实例的name属性是允许的。
很多时候,我们希望有的属性是无法直接存取的,这时候就可以用 private了:


class Animal {
    private name;
    public constructor(name) {
        this.name = name;
    }
}
let a = new Animal('Jack');
console.log(a.name);
a.name = 'Tom';
// index. ts(9,13): error TS2341: Property 'name' is private and only accessible within class " Animal'
// index. ts(10,1): error TS2341: Property 'name' is private and only accessible within class ' Animal'
复制代码

需要注意的是,TypeScript 编译之后的代码中,并没有限制private 属性在外部的可访问性。
-上面的例子编译后的代码是:


var Animal = (function () {
    function Animal(name) {
        this.name = name;
    }
    return Animal;
})();
var a = new Animal('Jack');
console.log(a.name);
a.name = 'Tom';
复制代码

使用private修饰的属性或方法, 在子类中也是不允许访问的:

class Animal {
    private name;
    public constructor(name) {
        this.name = name;
    }
}
class Cat extends Animal {
    constructor(name) {
        super(name);
        console.log(this.name);
    }
}
// index. ts(11,17): error TS2341: Property 'name' is private and only accessible within class ' Animal' 
复制代码

而如果是用 protected.修饰, 则允许在子类中访问:

class Animal {
    protected name;
    public constructor(name) {
        this.name = name;
    }
}
class Cat extends Animal {
    constructor(name){
    super(name);
    console.log(this.name);
}
}
复制代码

当构造函数修饰为`private’ 时, 该类不允许被继承或者实例化:


class Animal {
    public name: any;
    nane: string;
    private constructor(name: string) {
        this.nane = name;
    }
}

class Cat extends Animal {
    constructor(name: string) {
        super(name);
    }
}
let a = new Animal('Jack');
    // index.ts(7,19): TS2675: Cannot extend a class 'Animal'. Class constructor is marked as private 。
    // index.ts(13,9): TS2673: Constructor of class 'Animal' is private and  only accessible within the class dec laration.
复制代码

当构造函数修饰为 protected时, 该类只允许被继承:

class Animal {
    public name;
    protected constructor(name) {
        this.name = name;
    }
}
class Cat extends Animal {
    constructor(name) {
        super(name);
    }
}
let a = new Animal('Jack');
// index. ts(13,9): TS2674: Constructor of class ' Animal' is protected and only accessible within the class dec laration.
复制代码

参数属性

修饰符和readonly还可以使用在构造函数参数中,等同于类中定义该属性同时给该属性赋值,使代码更简洁。

class Animal{
// public name: string;
public constructor(public name) {
    // this.name = name;
}
}
复制代码

readonly

只读属性关键字,只允许出现在属性声明或索引签名或构造函数中。

class Animal {
readonly name;
public const ructor(name) {
this.name = name ;
}
let a = new Animal('Jack' );
console. Log(a.name); // Jack
a.name = 'Tom';
// index.ts(10,3): TS2540: Cannot assign to 'name' because it is a read-only property 
复制代码

注意如果 readonly和其他访问修 饰符同时存在的话,需要写在其后面。

class Animal {
    // public readonly name;
    public constructor(public readonly name) {
        // this.name = name ;
    }
}
复制代码

抽象类

abstract 用于定义抽象类和其中的抽象方法。
什么是抽象类?
首先,抽象类是不允许被实例化的:

abstract class Animal {
    public name;
    public constructor(name: string) {
        this.name = name;
    }
    public abstract sayHi();
}
let a = new Animal('Jack');

// index. ts(9,11): error TS2511: Cannot create an instance of the abstract class ' AnimaL'.
复制代码

上面的例子中,我们定义了一个抽象类Animal, 并且定义了一个抽象方法sayHi。在实例化抽象类的时候报错了。

其次,抽象类中的抽象方法必须被子类实现:

abstract class Animal {
    public name;
    public constructor(name: string) {
        this.name = name;
    }
    public abstract sayHi();
}
class Cat extends Animal {   
    public eat() {
        console.log(`${ this.name } is eating.` );
    }
}
let cat = new Cat('Tom');
// index.ts(9,7): error TS2515: Non-abstract class 'Cat' does not imp Lement inherited abstract member ' sayHi' from class ' Anima L'。
复制代码

上面的例子中,我们定义了一个类 Cat继承了抽象类Animal, 但是没有实现抽象方法sayHi,所以编译报错了。

下面是一个正确使用抽象类的例子:


abstract class Animal {
    public name;
    public constructor(name: any) {
        this.name = name;
    }
    public abstract sayHi();
}
class Cat extends Animal {
    public sayHi() {
        console.log(`Meow, My name is ${this.name}`);
    }
}
let cat = new Cat('Tom');
复制代码

写的太辛苦了,请各位点个赞吧,球球了

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享