这是我参与「第五届青训营」笔记创作活动的第10天。今天继续记录老师讲解的TypeScript基本知识。
一、本堂课重点内容:
TypeScript基本知识。
二、详细知识点介绍:
接口 Interface
在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。和 Java 一样,接口(Interfaces)是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。
TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。接口一般首字母大写。有的编程语言中会建议接口的名称加上 I 前缀。
接口的基本定义
一般的接口定义后,类型为该接口的实例必须严格对应类型。并且不允许少一些属性/多一些属性。
interface Person {
name: string;
age: number;
}
let viking: Person = {
name: 'viking',
age: 20,
}
可选属性
在属性定义中,可以用?来表示接口中的某个属性是可选项。该接口类型的实例可以没有该属性。
interface Person2 {
name: string;
age?: number;
}
let viking2: Person2 = {
name: 'viking',
}
任意属性
有时候我们希望一个接口允许有任意的属性,可以使用如下方式:
interface IPerson {
name: string;
age?: number;
[propName: string]: any; // 接口中含有任意属性
}
let tom: IPerson = {
name: 'Tom',
gender: 'male'
};
需要注意的是:
- 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:
- 一个接口中只能定义一个任意属性。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型:
interface IPerson2 {
name: string;
age?: number;
[propName: string]: string;
}
let tom2: IPerson2 = {
name: 'Tom',
age: 25,
gender: 'male'
};
在上面这个例子中,任意属性的值(即[propName: string]: string;)允许的类型集合是 string,但是可选属性 age 的值却是 number,number 不是 string 的子集,所以报错了。
只读属性 readonly
readonly表示接口中的某个属性是只读的,在定义的时候必须初始化,且在之后不能改变。和const的区别是,readonly用于属性,const用于变量。
interface Person3 {
readonly id: number;
name: string;
age?: number;
}
let viking3: Person3 = {
id: 123,
name: 'viking',
}
// viking3.id = 1234; // 报错:无法为“id”赋值,因为它是只读属性。
类 Class
个人感觉TypeScript的类和Class的类非常类似,主要区分就是构造函数是ES6的 constructor。
class Animal {
name: string;
private age: number;
constructor(name: string) { this.name = name; }
run() {
return `${this.name} is running`;
}
static isAnimal(o:any) {
console.log(o instanceof Animal);
}
}
const snake = new Animal("catlily");
类的属性默认是public,可以访问和修改
snake.name = "lucky";
类的private属性不可以在类的定义区域外访问和修改。甚至子类也无法访问
// snake.age // 属性“age”为私有属性,只能在类“Animal”中访问。
类似Java,类的protect属性不可以在类的定义区域外访问和修改。子类可以访问。
类的readonly属性是只读的,不可以修改。
类的静态属性和静态方法不需要实例化,可以直接调用。
Animal.isAnimal(snake);
和 Java 类似,TS中也有父子类继承 extend。
class Dog extends Animal {
bark() {
return `${this.name} is barking`;
}
}
const xiaobao = new Dog('xiaobao');
console.log(xiaobao.run());
console.log(xiaobao.bark());
class Cat extends Animal {
constructor(name: string) {
super(name);
console.log(this.name);
}
run() {
return 'Meow, '+super.run();
}
}
const cat = new Cat('Tim');
console.log(cat.run());
接口和类
和 Java 一样,类通过 implement 实现接口。自然也能同时实现多个接口。
interface Radio {
switchRadio(trigger: boolean): void;
}
interface Battery {
checkBattery(): void;
}
class Car implements Radio {
switchRadio(trigger: boolean): void {
throw new Error("Method not implemented.");
}
}
class CellPhone implements Radio, Battery {
switchRadio(trigger: boolean): void {
throw new Error("Method not implemented.");
}
checkBattery(): void {
console.log("Checking");
}
}
接口之间也可以相互继承。
interface RadioAndBattery extends Radio, Battery {}
class MobilePhone implements RadioAndBattery{
switchRadio(trigger: boolean): void {
throw new Error("Method not implemented.");
}
checkBattery(): void {
throw new Error("Method not implemented.");
}
}
函数
TS中的函数参数个数不对不会引发语法检查的报错,只会在编译运行时报错。
?可以表示函数的某个参数是可选参数。
function add(x: number, y: number, z?: number):number {
if (typeof z === 'number') {
return x + y + z;
}
return x + y;
}
let result = add(1, 2);
=表示函数默认值。拥有默认值的参数可以缺省。
function add2(x: number, y: number, z: number = 10):number {
return x + y + z;
}
let result2 = add2(1, 2);
let result3 = add2(1, 2, 3);
console.log(result, result2, result3);
函数类型的赋值。
const add3: (x: number, y: number, z: number) => number = add2;
console.log(add3(12, 23, 3)); // 只传递2个会在编译运行时报错
注意这里的箭头=>不是ES6的箭头函数,而是ts中声明函数类型返回值的方法。但是注意,这里add3的参数类型声明没有默认值的话,并不会继承 add2 的参数默认值。
三、实践练习例子:
例子已经在文章中举出。
四、课后个人总结:
本章的知识点需要大量的实例和参考资料来辅助理解。
五、引用参考:
我主要是基于老师讲解提供的代码仓库进行理解和分析,并记录了自己的心得。