一:认识ts
为什么要学习ts
- TypeScript 是由微软开发的
2.一款开源的编程语言。
-
TypeScript 是Javascript的超级,遵循最新的ES6、 Es5规范。 TypeScript扩展了JavaScript的语法。
-
TypeScript 更像后端java、 C#这样的面向对象语言可以让js开发大型企业项目。
5.谷歌也在大力支持Typescript 的推广,谷歌的angular2. x+就是基于Typescript语法。最新的Vue3.0、React 也可以集成TypeScript.
TypeSc ript的设计目的应该是解决JavaScript的“痛点” :
1.弱类型和没有命名空间,导致很难模块化,
2.不适合开发大型程序。
3.提供了一些语法糖来帮助大家更方便地实践面向对象的编程。(类, 接口,枚举,泛型,方法重载等)
- TypeScript的一个设计亮点就是它并没有抛弃JavaScript的语法另起炉灶,而是做成了JavaScript的超集(这个功劳应该记在Anders上),这样任何合法的JavaScript的语句在TypeScript下都是合法的,也就是说学习成本很低
ts的编译安装
- npm install -g typescript
- tsc 指向ts结尾的文件
借助vscode编辑器上面的Terminal
- tsc –init
- Run Task
3.选中typescript
4. tsc:watch
二:ts的原始数据类型
JavaScript的类型分为两种:原始数据类型和对象类型
原始数据类型包括:布尔值、数值、字符串、 null
、 undefined
以及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中的构造函数。其他基本类型(除了null
和undefined
)一样,不再赘述。
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
类型的变量没有什么用,因为你只能将它赋值为undefined
和null
:
let unusable: void = undefined;
复制代码
5: Null 和Undefined
在TypeScript 中,可以使用null
和undefined
来定义这两个原始数据类型:
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
的类型是 string
或 number
,但是不能是其他类型。
访问联合类型的属性或方法
当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;
}
复制代码
在这个例子中,我们除了约束当索引的类型是数字时,值的类型必须是数字之外,也约束了它还有length
和 callee
两个属性。
事实上常用的类数组都有自己的接口定义,如 IArgyments
,NodeList
,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
,private
和protected
。
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');
复制代码
写的太辛苦了,请各位点个赞吧,球球了