Skip to content

Latest commit

 

History

History
326 lines (244 loc) · 7.9 KB

9.进阶1.md

File metadata and controls

326 lines (244 loc) · 7.9 KB

类型别名

类型别名用来给一个类型起新的名字

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n:NameOrResolver):Name {
    if(typeof n === 'string'){
        return n;
    }else {
        return n();
    }
}

使用type创建类型别名,类型别名常用于联合类型

字符串字面量类型

字符串字面量类型用来约束值只能是某几个字符串中的一个

type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele:Element,event:EventNames) {
    // ...
}
handleEvent(document.getElementById('hello'),'scroll');
handleEvent(document.getElementById('world'),'dblclick'); // error

我们使用type定了一个字符串字面量类型 EventNames,它只能取三种字符串中的一种

类型别名与字符串字面量类型都是使用type进行定义

元组

// 定义一对值分别为 string和number 的元组:

let tom: [string, number] = ['Tom', 25];

当直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项

let tom:[string,number];
tom = ['tom',25];

tom = ['tom']; // error

越界的元素

当添加越界的元素时,它的类型会被限制为元组每个元素的联合类型

let tom: [string, number];
tom = ['Tom', 25];
tom.push('male');
tom.push(true);

枚举

枚举类型用于取值被限定在一定范围内的场景

枚举使用 enum 关键字来定义

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

// 编译为

var Days;
(function (Days) {
    Days[Days["Sun"] = 0] = "Sun";
    Days[Days["Mon"] = 1] = "Mon";
    Days[Days["Tue"] = 2] = "Tue";
    Days[Days["Wed"] = 3] = "Wed";
    Days[Days["Thu"] = 4] = "Thu";
    Days[Days["Fri"] = 5] = "Fri";
    Days[Days["Sat"] = 6] = "Sat";
})(Days || (Days = {}));

手动赋值

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

// 编译为
var Days;
(function (Days) {
    Days[Days["Sun"] = 3] = "Sun";
    Days[Days["Mon"] = 1] = "Mon";
    Days[Days["Tue"] = 2] = "Tue";
    Days[Days["Wed"] = 3] = "Wed";
    Days[Days["Thu"] = 4] = "Thu";
    Days[Days["Fri"] = 5] = "Fri";
    Days[Days["Sat"] = 6] = "Sat";
})(Days || (Days = {}));

手动赋值枚举项可以不是数字,但是需要使用类型断言来让tsc无视类型检查

enum Days {Sun = 7, Mon, Tue, Wed, Thu, Fri, Sat = <any>"S"};

// 编译为
var Days;
(function (Days) {
    Days[Days["Sun"] = 7] = "Sun";
    Days[Days["Mon"] = 8] = "Mon";
    Days[Days["Tue"] = 9] = "Tue";
    Days[Days["Wed"] = 10] = "Wed";
    Days[Days["Thu"] = 11] = "Thu";
    Days[Days["Fri"] = 12] = "Fri";
    Days[Days["Sat"] = "S"] = "Sat";
})(Days || (Days = {}));

手动赋值枚举项也可以是小数或者负数,此后递增步长仍为1

enum Days {Sun = 7, Mon = 1.5, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1.5); // true
console.log(Days["Tue"] === 2.5); // true
console.log(Days["Sat"] === 6.5); // true

常数项和计算所得项

枚举项有两种类型: 常数项和计算所得项

一个典型的计算所得项例子:

enum Color {Red, Green, Blue = "blue".length};
// "blue".length 就是一个计算所得项

如果紧跟在计算所得项后面是未手动赋值的项,那么它就会因为无法获得初始值而报错

enum Color {Red = "red".length, Green, Blue};
// error

常数枚举

常数枚举是使用 const enum定义的枚举类型

const enum Directions {
    Up,
    Down,
    Left,
    Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

// 编译为
var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];

如果包含了计算成员,则会在编译阶段报错

public private 和 protected

typescript可以使用三种访问修饰符:

  • public: 修饰的属性或方法是公有的,可以在任何地方被访问,默认所有属性和方法都是public
  • private: 修饰的属性和方法是私有的,不能再声明它的类的外部访问
  • protected: 修饰的属性和方法是受保护的,它和private类似,区别是它在子类中也是允许被访问的
class Animal {
    public name;
    private age;
    public constructor (name,age) {
        this.name = name;
        this.age = age;
    }
}

let a = new Animal('jack',18);
console.log(a.name); // jack
console.log(a.age) // error

编译后的代码,并没有限制private属性在外部的可访问性

使用private修饰的属性和方法,在子类中也是不允许访问的,而使用protected则允许在子类中访问

当构造函数修饰为private时,该类不允许被继承或者实例化,当构造函数修饰为protected时,该类只允许被继承

参数属性

修饰符和readonly还可以使用在构造函数参数中,等同于类中定义该属性同时给该数星星赋值

readonly

只读属性关键字,只允许出现在属性声明或索引签名或构造函数中

如果readonly和其他访问修饰符同时存在的话,需要写在其后面

抽象类

abstract用于定义抽象类和其中的抽象方法

首先,抽象类不允许被实例化

abstract class Animal {
    public name;
    public constructor(name) {
        this.name = name;
    }
    public abstract sayHi();
}

let a = new Animal('Jack'); // error

其次,抽象类中的抽象方法必须被子类实现

abstract class Animal {
    public name;
    public constructor(name) {
        this.name = name;
    }
    public abstract sayHi();
}

class Cat extends Animal {
    public eat() {
        console.log(`${this.name} is eating.`);
    }
}

let cat = new Cat('Tom'); // error

// 报错是因为没有实现抽象方法 sayHi

下面是一个正确使用抽象类的例子

abstract class Animal {
    public name;
    public constructor(name) {
        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');


// 编译为
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Animal = (function () {
    function Animal(name) {
        this.name = name;
    }
    return Animal;
}());
var Cat = (function (_super) {
    __extends(Cat, _super);
    function Cat() {
        _super.apply(this, arguments);
    }
    Cat.prototype.sayHi = function () {
        console.log('Meow, My name is ' + this.name);
    };
    return Cat;
}(Animal));
var cat = new Cat('Tom');