使用Typescript开发(二)

184 阅读4分钟

Typescript中其他所有类型可以有以下几类:

  1. 原生类型:比如Number,String,Boolean,Null,Void,Undefined以及Enum。
  2. Object类型:比如Function型,class和interface类型引用,array型,tuple型,function型,constructor型。
  3. 泛型:比如使用类型参数编写泛型
  4. 组合类型

原生类型

Enum类型

Enum(枚举)是用户自定义的原生类型,由一系列有名称的元素构成。属于Number的子类

enum STATUS {
  BEGIN,
  END,
  PAUSE
};

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

enum STATUS {
  BEGIN, // 0
  END,   // 1
  PAUSE  // 2
};

以上代码会编译成:

var STATUS;
(function (STATUS) {
    STATUS[STATUS["BEGIN"] = 0] = "BEGIN";
    STATUS[STATUS["END"] = 1] = "END";
    STATUS[STATUS["PAUSE"] = 2] = "PAUSE";
})(STATUS || (STATUS = {}));
;

然后可以这么使用

enum STATUS {
  BEGIN,
  END,
  PAUSE
};
var a: number = 1;
if (a === STATUS.END) {
  console.log('stauts is end');
}

手动赋值

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

enum STATUS {
  BEGIN = 3,
  END = 1,
  PAUSE
};

会生成:

var STATUS;
(function (STATUS) {
    STATUS[STATUS["BEGIN"] = 3] = "BEGIN";
    STATUS[STATUS["END"] = 1] = "END";
    STATUS[STATUS["PAUSE"] = 2] = "PAUSE";
})(STATUS || (STATUS = {}));
;

未手动赋值的枚举项会接着上一个枚举项递增。

Object类型

Array类型

Typescript里数组中不能容纳不同类型的元素。

  1. 定义一个数值型的数组:
let arr: number[] = []
arr.push(1);
arr.push(2);
console.log(arr); // [ 1, 2 ]
  1. 定义多种对象类型的数组,类似与JS中数组一样,使用any:
let arr: any[] = []
arr.push(1);
arr.push('2');
arr.push({});
console.log(arr); // [ 1, '2', {} ]

至于数组的方法,Typescript的使用方法和Javascript的方法一样:

arr.join('');
arr.splice(1, 0);
arr[0];

Function类型

在JavaScript中创建新的函数,可以使用剪头函数语法:

const getDay = time => {
  const date = new Date(time);  
  const day = date.getDate();
  return day;
};

Typescript赋予修改函数参数的类型返回值的类型的权利。编译器在类型比较和翻译后,所有类型注解都会被删掉。

函数表达式

使用函数表达式把一个函数赋给一个变量,那么可以这样定义:

let variable: (arg1:type1, arg2: type2 ...) => returnType

例如:

const getDay: (time: number) => number = time => {
  const date = new Date(time);
  const day = date.getDate();
  return day;
}
console.log(getDay(Date.now())); // 23

函数声明

如果采用函数声明的方式,要这么写:

function getDay(time: number): number {
  const date = new Date(time);
  const day = date.getDate();
  return day;
}
console.log(getDay(Date.now())); // 23

对象字面量

如果需要在对象字面量定义方法,可以这么写:

const Day = {
  getDay(time: number): number {
    const date = new Date(time);
    const day = date.getDate();
    return day;
  }
}
console.log(Day.getDay(Date.now())); // 23

void

如果我们定义的函数不需要返回值而是产生一些副作用,可以将它定义为void函数:

let dog = {
  say(name: string): void {
    console.log(name);
  }
}
console.log(dog.say('hi')); // hi 

定义类

ES6给类添加了更多语法特性,例如可以声明静态属性和实例属性。

class Human {
  static totalHumans = 0;
  _name;
  constructor(name) {
    this._name = name;
    Human.totalHumans++;
  }
  set(val) {
    this._name = val;
  }
  get() {
    return this._name;
  }
  talk() {
    return `I am ${this._name}`
  }
}
class Developer extends Human {
  _languages;
  constructor(name, language) {
    super(name);
    this._languages = language;
  }
  get language() {
    return this._languages;
  }
  talk() {
    return `${super.talk()}, and I can speak ${this._languages}`
  }
}
const people = new Human('Steven');
console.log(people._name); // Steven

const person = new Developer('Jasonho', 'English');
console.log(person.talk()); // I am Jasonho, and I can speak English

以上代码是合法的Javascript代码,但是使用Typescript方式也不会有什么区别,只是Typescript修改了类型声明并且有更多语法糖。比如:

class Human {
  static totalHumans = 0;
  _name: string;
  ...

使用访问修饰符

Typescript添加访问修饰符,目的就是帮助我们强制进行更好的封装,并且产生违反规则时运行的错误。 上面例子,发现可以在类定义的外部访问到_name属性,如果禁止访问应该怎么做?

可以把它定义为private访问修饰符

class Human {
  static totalHumans = 0;
  private _name: string;
  ...

控制台打印出:Property '_name' is private and only accessible within class 'Human'.

Typescript支持3种访问修饰符:

访问修饰符 访问范围
Public 在任何地方可以访问
Private 只能在类定义内部进行访问
Protected 可以在类定义内部进行访问,也可以从子类中访问

将上面带有继承结构的例子改造一下:

class Human {
  static totalHumans = 0;
  constructor(protected name: string, private age: number) {
    Human.totalHumans++;
  }
  talk() {
    return `I am ${this.name}, I am ${this.age}`
  }
}


class Developer extends Human {
  constructor(name: string, private languages: string[], age: number) {
    super(name, age);
  }
  talk() {
    return `${super.talk()}, and i know ${this.languages.join(',')}`;
  }
}
  1. Human构造函数,参数中设置了protected型的属性name, private型的属性age。这样可以避免在构造函数内部进行显示的赋值操作
  2. Developer构造函数,构造函数的语法可以混合使用——可以显示定义属性,也可以只定义构造函数接收的参数类型。

创建Human类的一个新实例,然后在外部访问内部属性:

const person = new Human('Jasonho', 24);
console.log(person.age);
console.log(person.name);

会抛出两条异常:

  1. Property 'age' is private and only accessible within class 'Human'.(age是私有属性,只能在Human内部访问。)
  2. Property 'name' is protected and only accessible within class 'Human' and its subclasses.(age是受保护的,只能在Human内部访问或者子类访问。)

同理,Developer类也是一样。

接口

接口目前在JavaScript是不存在的语法,那么接口是什么?用一句话概括就是:定义对象的类型

我们把所有子类的实例看作相同的对象看待,因为子类都是某个一般化对象的特殊版本。同时,这些对象会存在一些共同的属性,我们可以把相同的属性看成共同的东西对待。

如果对象实现了接口,而这些接口声明了他们具有某些特定的属性和行为,那么就可以确保对象拥有了这些属性和方法。

接口和继承的关系

  1. 接口interface:声明成员方法,不做具体实现;
  2. 继承extend:类声明并实现方法。

举个简单例子:

interface Accountable {
  getIncome(): number,
  accountNumber: number
}
class Firm implements Accountable {
  accountNumber = 123;
  getIncome(): number {
    return this.accountNumber
  }
}

var a = new Firm()
console.log(a.getIncome()) // 123

如果定义了一个接口,那么必须实现该接口里面所定义的所有方法,方法的实现也要与接口定义的签名完全一致,不然编译器会抛出错误。

class Individual implements Accountable {
}

类Individual没有实现Accountable的getIncome方法。编译时抛出错误:

接口继承

接口互相继承如何做到?

试将上面Firm这个类改为接口,它有一个age年龄属性:

interface Firm extends Accountable {
  age: number
}

多个接口

如果一个类的行为是很多接口中定义的行为的并集,那么可以用逗号隔开:

class Firm implements Accountable, Building, Human {
  accountNumber = 123;
  getIncome(): number {
    return this.accountNumber
  }
}