Class是JavaScript ES6以後出來的語法糖,今天大部分的內容也和JavaScript差不多,如果對JavaScript Class熟悉的話,加上型別就能掌握Class在TypeScript的基本用法了。
在TypeScript的語法裡,Class加上型別的最基本用法如下:
class Person {
id: number;
first_name: string;
last_name: string;
birth_year: number;
constructor(id: number, first_name: string, last_name: string, birth_year: number) {
this.id = id;
this.first_name = first_name;
this.last_name = last_name;
this.birth_year = birth_year;
}
getName(): string {
return `${this.first_name} ${this.last_name}`;
});
getBirthYear(): number {
return this.birth_year;
}
introduce(): string {
return `My name is ${first_name}.`;
}
}
const Bob = new Person(1234567, 'Bob', 'Jonason', 1989);
console.log(Bob.getName()); // Bob Jonason
console.log(Bob.birth_year); // Compile error
console.log(Bob.getBirthYear()); // 1989
任一類別都可以透過繼承(inheritance)的方式去重複使用特定類別擁有的屬性和方法,這裡創建一個SuperHero類別繼承剛剛的Person類別,在TypeScript一樣是使用 extends
關鍵字去繼承:
class SuperHero extends Person {
constructor(id: number, first_name: string, last_name: string, birth_year: number, title: string, ability: string){
super(id, first_name, last_name, birth_year);
this.title = title;
this.ability = ability;
}
introduce(): void {
return `${super.introduce()} I'm a ${this.title}`
}
}
const superMan = new SuperHero('00000000', 'Clark', 'Kent', 1938, 'super man', 'fly')
這個範例使用ES6的語法,為了讓SuperHero繼承Person的語法,所以在constructor裡用 super()
去使用父類別(parent class)Person既有的屬性和方法。
同時,這裡也用了 method overriding 的方式去覆寫Person類別的 introduce
,以及使用 super.introduce()
語法去存取Person類別既有的 introduce
函式回傳值。
上述class的屬性和方法都是專指特定物件(或說實例, instance)本身的方法和屬性,例如 first_name = 'Bob'
是屬於Bob物件的屬性和其屬性值。
但是不同物件(實例)之間可能也會有一些共用的屬性和方法,而跟單一物件(實例)自身沒有關係,這時可以用 static
關鍵字來創造這些共用屬性和方法:
class Person {
static person_count: number = 0;
id: number;
first_name: string;
last_name: string;
birth_year: number;
constructor(id: number, first_name: string, last_name: string, birth_year: number) {
this.id = id;
this.first_name = first_name;
this.last_name = last_name;
this.birth_year = birth_year;
++person_count;
}
static getPersonCount(): number{
return Person.person_count;
}
getName(): string {
return `${this.first_name} ${this.last_name}`;
});
getBirthYear(): number {
return this.birth_year;
}
introduce(): string {
return `My name is ${first_name}.`
}
}
在前面的例子,我們都是用 get...
開頭的函式去存取class裡面的 private
或 protected
屬性,但也有其他既能保護屬性,也能加上一些判斷避免屬性值被惡意竄改的方式,以下使用 getter 重寫和 getBirthyear
相同的方法,並且使用 setter 加上修改 birth_year 的方法:
class Person {
static person_count: number = 0;
constructor(id: number, first_name: string, last_name: string, birth_year: number) {
this.id = id;
this.first_name = first_name;
this.last_name = last_name;
this.birth_year = birth_year;
++person_count;
}
static getPersonCount(): number{
return Person.person_count;
}
getName(): string {
return`${this.first_name} ${this.last_name}`;
});
/*
getBirthYear(): number {
return this.birth_year;
}
*/
get birthYear(): number {
return this._birth_year;
}
set birthYear(birth_year: number) {
if(birth_year >= 0) {
this._birth_year = birth_year;
} else {
throw new Error("Error: Invalid birth year");
}
}
introduce(): string {
return `My name is ${first_name}.`
}
}
const Bob = new Person(1234567, 'Bob', 'Jonason', 1989);
console.log(Bob.birthYear); // 1989
Bob.birthYear = -1; // Error: Invalid birth year
Bob.birthYear = 1990;
console.log(Bob.birthYear); // 1990
這個範例不允許負的出生年(或說西元前的出生年),因此加上 >=0
的限制來避免。
而從範例也可以看出相較於寫一般函式,getter和setter的寫法更為簡潔有力,也確實能保護屬性。
今天談的大都是在JavaScript就有的語法,只是加上型別讓這些語法可以在TypeScript通過編譯器,並強化class本身的屬性和方法,明天才會專注於討論TypeScript在class新增的語法。