元組
在TypeScript中,有元組的概念,這個概念的js實作是數組,是以可以了解為是有不同類型對象的數組。
let a:[number,string];
a = [55,'25']
這樣是元組的基本用法,有基本用法,那就一定有奇怪的用法。
我們可以通過一定方法對任意一個進行指派,而不管另一個
let a:[number,string];
a[1] = '25'
但是我們不可以在對a直接指派的時候,給出不符合規則的值
let a:[number,string] = 55;
這樣是無法通過編譯的。
既然是數組了,那麼可不可以繼續添加對象進去呢?當然是可以的,但是需要是規定的類型才行,比如上面我們規定元組的第一個元素是number類型,第二個是string類型,那麼後續的隻能是number、string或者他們的子類型。
字元串字面量類型和枚舉類型
這兩個為什麼要放到一起說呢?因為他們都限定了在這個類型内,可以選擇的字元串隻有有限個,是以兩個放到一起說一下。
字元串字面量類型
這個類型特别簡單,就是在自己設定的類型中,隻有有自己設定的值。
type Name = 'xiaoming' | 'xiaohong' | 'xiaoliang';
function call(name: Name){
//call
}
call('xiaoming'); //可以編譯
call('wo'); //不能編譯
這就是字元串字面量類型,沒有任何變化。
枚舉
枚舉類型被用于取值限定在一定範圍内的場景,用途類似上面的字元串字面量類型。
enum Week {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 0); // true
console.log(Days[0] === "Sun"); // true
這樣就是一個枚舉類型了,他在js的實作是
var Week;
(function (Week) {
Week[Week["Sun"] = 0] = "Sun";
Week[Week["Mon"] = 1] = "Mon";
Week[Week["Tue"] = 2] = "Tue";
Week[Week["Wed"] = 3] = "Wed";
Week[Week["Thu"] = 4] = "Thu";
Week[Week["Fri"] = 5] = "Fri";
Week[Week["Sat"] = 6] = "Sat";
})(Week || (Week = {}));
這個實作沒什麼好說的,看一眼應該就知道他的原理是怎麼回事了,如果覺得有點抽象,可以再浏覽器裡列印一下Week這個對象,就明白這裡的枚舉類型是什麼意思了。
枚舉類型還可以手動指派
enum Week {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 7); // true
console.log(Days["Tue"] === 2); // true
枚舉類型的需要會按照以最後一個設定值為基礎,每一個值+1的順序往下排,即使最後一個設定值不為整數也是一樣。
還可以用斷言添加非數值的序數,但是這個斷言後面的所有序數都要手動設定了。
enum Days {Sun = 7, Mon, Tue, Wed, Thu, Fri, Sat = <any>"a"};
除了固定值的序号,還可以用計算量做需要,但是計算量和字元串序号一樣,後面都不能有未設定序号了。
類
在es6之前,JavaScript沒有類的概念,但是在es6中,出現了類,同樣TypeScript中也有對類的規定,這裡的規定很像java中類的規則。
修飾符
TypeScript中有三個修飾符,分别是public,private,protected。
public是公開的,可任意通路的,
private是私有的,隻能在類内部通路,他的子類和外部是不能通路的。
protected是受保護的,同樣不能再外部通路,但是可以再子類和内部通路。
class Animal {
public name;
public constructor(name) {
this.name = name;
}
}
let a = new Animal('Cat');
console.log(a.name); // Cat
這樣的通路是沒有問題的
class Animal {
private name;
public constructor(name) {
this.name = name;
}
}
let a = new Animal('Cat');
console.log(a.name);
這樣編譯就會報錯,因為私有屬性是不可以在外部被通路的。
class Animal {
protected name;
public constructor(name) {
this.name = name;
}
}
class Cat extends Animal {
constructor(name) {
super(name);
console.log(this.name);
}
}
受保護的屬性是可以再子類中被通路,但是不能在外部通路。
抽象類
所謂抽象類,就是不能執行個體化的類,這種類就是為了子類而存在的,他們的子類可以執行個體化。
抽象類還有抽象方法,抽象方法可以再抽象類中定義,不去做實作,在子類中必須實作抽象方法。關鍵字是abstract
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');
這樣會報錯,因為還有一個抽象方法沒有寫實作。
類與接口
接口的實質是對象的形狀,那麼接口可以規定對象,可以規定類嗎?
答案是啊可以的
interface Alarm {
alert();
}
class Door {
}
class SecurityDoor extends Door implements Alarm {
alert() {
console.log('SecurityDoor alert');
}
}
class Car implements Alarm {
alert() {
console.log('Car alert');
}
}
這裡抽象出來一個接口,叫做警鈴。門有門鈴功能,車有警鈴功能,那麼把這兩個類共有的東西抽象出來,就是類的接口。
接口規定,這個類必須有這個屬性,至于能不能有其他的東西,接口不管。如果類可以抽象出來多個接口,那麼也可以使用多個接口規定同一個類
interface Alarm {
alert();
}
interface Light {
lightOn();
lightOff();
}
class Car implements Alarm, Light {
alert() {
console.log('Car alert');
}
lightOn() {
console.log('Car light on');
}
lightOff() {
console.log('Car light off');
}
}
接口還有一種很特殊的繼承性,子接口可以繼承父接口的規則。
interface Alarm {
alert();
}
interface LightableAlarm extends Alarm {
lightOn();
lightOff();
}
個人不是很喜歡這種繼承,确實可以再項目擴大之後,寫的更加簡略比如上面的
class Car implements Alarm, Light {
alert() {
console.log('Car alert');
}
lightOn() {
console.log('Car light on');
}
lightOff() {
console.log('Car light off');
}
}
此時可以簡寫為
class Car implements Light {
alert() {
console.log('Car alert');
}
lightOn() {
console.log('Car light on');
}
lightOff() {
console.log('Car light off');
}
}
但是這時如果Light是多層的嵌套的時候,你就會很疑惑他的規則到底是什麼。想要查清楚需要追溯到最上面一層,就好像一個樹,你得看完他所有節點才能清楚的知道全部,對于ts的檢查來說自然無所謂,但是對于寫代碼的人來說,就很煩,尤其是在沒有完善的文檔的情況下。
接口還可以直接從類裡繼承
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
對這種東西,個人表示也不太喜歡,理由基本同上。