TypeScript基本知识小结(2) | 青训营笔记

79 阅读4分钟

这是我参与「第五届青训营」笔记创作活动的第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'
};

需要注意的是:

  1. 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:
  2. 一个接口中只能定义一个任意属性。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型:
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 的参数默认值。

三、实践练习例子:

例子已经在文章中举出。

四、课后个人总结:

本章的知识点需要大量的实例和参考资料来辅助理解。

五、引用参考:

我主要是基于老师讲解提供的代码仓库进行理解和分析,并记录了自己的心得。