TypeScript基础重温系列(二)

410 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

1. 数组和元组

  • 数组的类型注解可以为基础类型或者对象类型
  • 元组:即元素个数有限的数组,此类型在处理csv文件时较为有用
// 数组的类型注解如下:既可以为基础类型,也可以为对象类型
const arr: (number | string)[] = [1, '2', 3];
const stringArr: string[] = ['a', 'b', 'c'];
const undefinedArr: undefined[] = [undefined];

// type alias 类型别名
type User = { name: string; age: number };
const objectArr: User[] = [
  {
    name: 'dell',
    age: 28
  }
];

class Teacher {
  name: string;
  age: number;
}
const objectArr: Teacher[] = [
  new Teacher(),
  // 下面这个对象虽然不是Teacher的实例对象,但是只要数据结构和Teacher实例对象的相同就可以
  {
    name: 'dell',
    age: 28
  }
];

// 元组 tuple(元组即元素个数有限的数组)
const teacherInfo: [string, string, number] = ['Dell', 'male', 18];
// 元组在处理csv文件时比较有用,如下定义一个数组,数组中每个元素是元组
const teacherList: [string, string, number][] = [['dell', 'male', 19], ['sun', 'female', 26], ['jeny', 'female', 38]];

2. Interface接口

类型别名(type)和接口(interface)的区别:类型别名可以代表基础类型或者对象,而接口只能代表对象。

在typescript中,有一个规范:如果能用接口去表示类型就用接口,实在不行才用类型别名。

// interface 和 type 相类似,但并不完全一致
interface Person {
  // readonly name: string;  // readonly只读不能修改
  name: string;
  age?: number; // 接口定义中的?表示该属性可有可无
  [propName: string]: any;// 表示可以接收任何额外属性
  say(): string; // 这是一个方法,返回值类型是string
}

interface Teacher extends Person {
  teach(): string;
}

// 接口除了以上常用的定义变量、方法之外,还可以定义函数类型。如下定义一个函数名为SayHi、参数和返回值均为string类型的函数
interface SayHi {
  (word: string): string;
}

const getPersonName = (person: Person): void => {
  console.log(person.name);
};

const setPersonName = (person: Teacher, name: string): void => {
  person.name = name;
};

const person = {
  name: 'dell',
  sex: 'male',
  say() {
    return 'say hello';
  },
  teach() {
    return 'teach';
  }
};

getPersonName(person);// 如果是直接传一个{xx:xx}对象字面量参数的话,则不能有多余属性,否则报错。用person变量可以有多余属性,且不会报错。(解决方法:在Person接口定义中加上[propName: string]: any;)
setPersonName(person, 'lee');

class User implements Person { // 类实现接口时,必须给其必须有的变量和方法进行赋值
  name = 'dell';
  say() {
    return 'hello';
  }
}

const say: SayHi = (word: string) => {
  return word;
};

在编译成js代码后,接口和类型相关的内容会被剔除掉,只是在开发过程中ts会帮助我们做语法提示。

3. 类的定义与继承

class Person {
  name = 'dell';
  getName() {
    return this.name;
  }
}

// 类的继承
class Teacher extends Person {
  getTeacherName() {
    return 'Teacher';
  }
  // 重写父类中的方法
  getName() {
    // super代表父类
    return super.getName() + 'lee';
  }
}

const teacher = new Teacher();
console.log(teacher.getName());
console.log(teacher.getTeacherName());

super的作用:在子类重写父类的方法时,如果需要在重写方法中调用父类的方法,则可以用super代表父类去调用父类的方法。

4. 类中的访问类型和构造器

访问类型
  • public:允许我在类的内外被调用
  • private:允许在类内被使用
  • protected:允许在类内及继承的子类中使用
// private, protected, public 访问类型
class Person {
  public name: string;
  public sayHi() {
    this.name;
    console.log('hi');
  }
  private sayABC() {
    this.name;
  }
}

class Teacher extends Person {
  public sayBye() {
    this.sayHi();
  }
}

const person = new Person();
person.name = 'dell';
console.log(person.name);
person.sayHi();

构造器
// constructor
// class Person {
//   // 传统写法
//   // public name: string;
//   // constructor(name: string) {
//   //   this.name = name;
//   // }
//   // 简化写法(常用)
//   constructor(public name: string) {}
// }
// const person = new Person('dell');
// console.log(person.name);

class Person {
  constructor(public name: string) {}
}

class Teacher extends Person {
  constructor(public age: number) {
    super('dell');
  }
}

const teacher = new Teacher(28);
console.log(teacher.age);
console.log(teacher.name);

如果父类有构造器,子类在声明构造器时一定要用super手动调用父类的构造器,否则会报错。如果父类没有构造器,也需要调用一个空的super()

5. Getter和Setter,静态属性

getter and setter

getter and setter起到保护私有变量的作用。一般类的私有变量的命名会以下划线开头

class Person {
  constructor(private _name: string) {}
  get name() {
    return this._name + ' lee';
  }
  set name(name: string) {
    const realName = name.split(' ')[0];
    this._name = realName;
  }
}

const person = new Person('dell');
console.log(person.name);
person.name = 'dell lee';
console.log(person.name);
readonly

除了上述的用set和get方法来保护私有变量(只能读不能修改)之外,还可以用readonly关键字

// readonly
class Person {
  public readonly name: string;
  constructor(name: string) {
    this.name = name;
  }
}

const person = new Person('Dell');
person.name = 'hello'; // 这里会提示报错
console.log(person.name);
静态属性

static是直接挂在类上的,而不是挂在实例上

// 如下是单例模式的例子(单例模式:一个类只允许获取一个这个类的实例)
class Demo {
  private static instance: Demo;
  private constructor(public name: string) {}

  static getInstance() { // static:把方法直接挂在类上,而不是实例上,可以通过类名.xxx去调用
    if (!this.instance) {
      this.instance = new Demo('dell lee');
    }
    return this.instance;
  }
}

const demo1 = Demo.getInstance();
const demo2 = Demo.getInstance();
console.log(demo1.name);
console.log(demo2.name);

6. 抽象类

抽象类只能被继承,不能被实例化。且继承的时候必须实现所有抽象方法。

抽象类和接口有点像,抽象类是把类相关的通用东西抽象出来,而接口可以把对象相关的通用东西抽象出来。

// 抽象类
abstract class Geom {
  width: number;
  getType() {
    return 'Gemo';
  }
  abstract getArea(): number;
}

class Circle extends Geom {
  getArea() {
    return 123;
  }
}

class Square {}
class Triangle {}
// 以下是interface的继承
interface Person {
  name: string;
}

interface Teacher extends Person {
  teachingAge: number;
}

interface Student extends Person {
  age: number;
}

interface Driver {
  name: string;
  age: number;
}

const teacher = {
  name: 'dell',
  teachingAge: 3
};

const student = {
  name: 'lee',
  age: 18
};

// 通过接口的继承可以简化ts的代码
const getUserInfo = (user: Person) => {
  console.log(user.name);
};

getUserInfo(teacher);
getUserInfo(student);

下一篇:TypeScript语法进阶重温系列(三)