TypeScript 從據說到入門(上篇)

我爲何會這樣念念又不忘 / 你用什麼牌的箭刺穿我心臟javascript

我也久經沙場 / 戎馬生涯 / 依然 / 被一箭刺傷java

——李榮浩《念念又不忘》typescript

接下來我會分上、下兩篇文章介紹 TypeScript。shell

我也是 TypeScript 初學者,這兩篇文章是個人學習筆記,來源於一個系列的免費視頻,視頻不錯,若是你以爲看視頻學習更快,能夠點擊這裏看到npm

TypeScript 與 JavaScript 有何區別呢?簡單點說,就是前者引入了類型約束,能實現代碼的靜態檢查,並且能提供更加完善的代碼提示功能;除此以外,還引入了接口、抽象類、枚舉、訪問控制修飾符、泛型等語法,讓咱們寫出的代碼更具健壯性和可擴展性。編程

上、下兩篇的文章內容安排以下:數組

  • 《上篇》
    1. 基本類型
    2. 對象類型
    3. 擴展類型
  • 《下篇》
    1. 面向對象編程
    2. 泛型

《上篇》定義爲基礎篇,《下篇》定義爲深刻篇。bash

咱們先從基礎篇開始學習。函數

開發環境

TypeScript 腳本以 .ts 後綴結尾。我使用 VSCode 學習 TypeScript,寫好 TypeScript 代碼後, 須要將其編譯爲 JavaScript 代碼才能運行。在此以前,咱們先要安裝 Node.js 環境。學習

而後安裝 TypeScript 編譯環境

$ npm install -g typescript
$ tsc --version
複製代碼

編譯文件的指令以下:

// 執行完下列語句後,會在同級目錄下看到一個 `HelloWorld.js` 文件,
// 就是編譯完成後的文件啦
$ tsc HelloWorld.ts
複製代碼

codepen.io 中也支持 TypeScript 的書寫。新建一個新的 Pen 後,將 JavaScript 一欄的預處理器設置成 TypeScript 便可,你也能夠實時查看到編譯以後的代碼,不過代碼提示效果不是很強。

基本類型

咱們先從最簡單的基本數據類型(Primitive)提及。

ECMAScript 提供了六種基本數據類型:布爾、數值、字符串、Null、Undefined 和 Symbol。

而 TypeScript 針對上述的每一種類型,都提供了對應的類型字面值:boolean、number、string、null、undefiend 和 symbol(ES6 中引入,本系列兩篇不對 symbol 作介紹)。

在 TypeScript 中,使用 : type 語法爲變量指定類型:

// 聲明一個布爾值類型變量 `isMale`,初始值爲 `true`
let isMale: boolean = true;
// 聲明一個字符串類型變量 `myName`,初始值爲 `'Alex'`
let myName: string = 'Alex';
// 聲明一個數值類型變量 `myAge`,初始值爲 `20`
let myAge: number = 20;
// 聲明一個 Null 類型變量 `myGirlFriend`,值爲 `null`
let myGirlFriend: null = null;
// 聲明一個 Undefined 類型變量 `myHouse`,值爲 `undefined`
let myHouse: undefined = undefined;
複製代碼

注意:TypeScript 提供的類型字面值都是小寫形式,注意與首字母大寫的形式區分,後者是 JavaScript 原生提供的構造器函數。

有時,一個變量的類型並不侷限於一種。好比,一個變量的值能夠是字符串,也能夠是數值。這時就要用到「聯合類型」了。

聯合類型使用豎線 | 分隔,表示某個變量能夠給予其中任意一種類型值。

下例中,聲明瞭一個變量 foo,它的值能夠是一個字符串,也能夠是一個數值。

// 此處聲明瞭一個變量 `foo`,能夠是字符串,也能夠是數值
let foo: string | number = 'bar'; // 初始值給了字符串 `'bar'`
foo = 123; // 接下來將 `foo` 從新賦值爲 `123`
複製代碼

對象類型

除了基本類型,工做中最常處理就是對象了。那麼如何在 TypeScript 中指定對象類型呢?

定義對象

在 TypeScript 中,使用接口,也就是關鍵字 interface 來描述對象的形狀,也就是對象的類型。「接口」在傳統的面向對象編程的語言裏,好比 Java,表示「行爲的抽象」,而在 TypeScript 對此稍有不一樣,接口不只能夠表示行爲的抽象,還能夠用來定義對象類型。

接下來,咱們定義一個類型 Person(按照約定,首字母大寫):

// 用接口聲明一個類型 `Person`
interface Person {
    name: string;
    age: number;
}

// 將變量 `alex` 聲明爲 `Person` 類型
let alex: Person = {
    name: 'Alex',
    age: 20
};
複製代碼

注意:定義接口時,屬性之間能夠用分號 ;、也能夠用逗號 , 分隔,甚至什麼都不加也能夠。

咱們定義了一個類型 Person,並將變量 alex 的類型聲明爲 Person。那麼,在給 alex 賦值時,必須嚴格符合類型定義:賦值對象必須由一個字符串屬性 name和一個數值屬性 age 組成,缺乏或多出的屬性,都會提示錯誤。

// 會提示出錯(缺乏一個屬性)
let alex: Person = {
    name: 'Alex';
};

// 會提示錯誤(多了一個屬性)
let alex: Person = {
    name: 'Alex',
    age: 20,
    gender: 'male'
};
複製代碼

定義類型時,若是想要表示某個屬性是可選的,則使用 ?: type 語法聲明。

// 類型 `Person` 的 `name` 屬性是可選的 
interface Person {
    name: string;
    age?: number;
}

// 由於 `age` 是可選屬性,因此賦值時不給也行
let alex: Person = {
    name: 'Alex'
};
複製代碼

除了定義可選屬性,還能夠定義「任意屬性」。

所謂的任意屬性,就是咱們不肯定未來會添加的屬性名稱是什麼,可是會提早定義容許添加的屬性,在不肯定將來這個屬性名的狀況下,限制這個屬性的類型。

// Person 中定義了一個任意屬性,屬性類型是 `any`
interface Person {
    name: string;
    age?: number;
    [propName: string]: any;
}

// 咱們給變量 `alex` 添加了一個任意屬性 `gender`
let alex: Person = {
    name: 'Alex',
    gender: 'male
};
複製代碼

咱們使用 [propName: string]: any 的形式,定義了一個 any 類型的任意屬性。

注意,任意屬性的類型,必須是上面的已知屬性 nameage 類型的超集,不然會提示出錯。 好比,上面咱們能夠將上面任意屬性的類型 any 修改成 string | number 也是能夠的。

說完對象,再來介紹數組。

數組類型

在數組上指定類型,本質上是限制數組成員的類型。在 TypeScript 中,使用 type[] 語法指定數組成員的類型。

下面定義了一個數組,限制其成員只能是字符串。

// 此處定義了一個數組 `myFriends`,其成員限定爲只能是字符串
let myFriends: string[] = ['Alex', 'Bob'];
複製代碼

若是數組成員容許包含多個類型值,則使用 (type1 | type2 | ...)[] 的語法聲明。

// 此處定義了一個數組 `foo`,其成員能夠是字符串,也能夠是數值
let foo: (string | number)[] = ['Alex', 'Bob', 123];
複製代碼

若是數組的成員是對象,則有以下兩種聲明方式:

// 方式 1:經過預約義好的類型,聲明 `friends` 成員類型
interface Person {
    name: string;
}
let friends: Person[] = [ { name: 'Alex' }, { name: 'Bob' } ];

// 方式 2:直接經過字面量類型的形式,聲明 `friends` 成員類型
let friends: {
    name: string
}[] = [ { name: 'Alex' }, { name: 'Bob' } ];
複製代碼

接下來,進入到擴展類型的學習。

擴展類型

Typescript 除了支持 JavaScript 類型以外,還提供了一些擴展類型。

首先,咱們來介紹下字面量類型。

字面量類型

當咱們像下面這樣賦值時:

let seven: number = 7;
// ❌ 這樣賦值的話會有錯誤,提示`Type '"Seven"' is not assignable to type 'number'`
seven = 'Seven';
複製代碼

注意,這裏的 'Seven' 被當成了一個類型,說 'Seven' 類型不能賦值給數值類型變量 seven

其實這裏的 'Seven' 是一個字符串字面量類型。

Typescript 中的字面量類型包括:字符串字面量、數值字面量和布爾值字面量。

除此以外,咱們還可使用 type 關鍵字定義一個新的類型:

// 此處咱們定義了一個新類型 `FavoriteNumber`,這個新類型僅由三個值的集合組成
type FavoriteNumber = 'One' | 'Two' | 'Seven';
// 接下來,將變量 `seven` 聲明爲類型 `FavoriteNumber`,並賦值爲 `'Seven'`
let seven: FavoriteNumber = 'Seven';
複製代碼

以上定義了一個類型 FavoriteNumber,它由三個值的集合組成(一個類型一般至少包含兩個或以上的值)。變量 seven 被聲明爲該類型,賦值爲 'Seven',這是一個有效值。若是咱們給 seven 賦了一個不在 FavoriteNumber 類型以內的值,就會報錯,好比:

// ❌ 此處會報錯:`Type '123' is not assignable to type 'FavoriteNumber'.`
let seven: FavoriteNumber = 123;
複製代碼

枚舉

在介紹枚舉類型以前,咱們先來看下面的代碼:

// 此處定義了兩個變量 `errorColor` 和 `infoColor`
let dangerColor = 'red';
let infoColor = 'blue';

// 添加一個判斷傳入顏色是不是危險色的函數
function isItDangerColor(color) {
    return color === 'red';
}

// 接下來,調用函數 `isItRed`
isItDangerColor(dangerColor); // true
isItDangerColor(infoColor); // false
複製代碼

上面這一小段的代碼邏輯很簡單,但有個小小的問題——若是表示危險的顏色由 'red' 變爲 'pink' 了,那麼咱們就須要修改兩個地方的代碼。

針對這個問題,咱們稍微修改下代碼,引入一個表示顏色集合的變量 Colors 來解決:

// 咱們使用 `Colors` 這個變量來存儲邏輯中使用到的顏色集合
const Colors = {
    Danger: 'red',
    Info: 'blue'
};

// 在餘下的業務邏輯中,咱們使用顏色變量代替以前的顏色字面值
let dangerColor = Colors.Danger;
let infoColor = Colors.Info;

function isItDangerColor(color) {
    return color === Colors.Danger;
}
複製代碼

這樣帶來的便利是,若是 Colors.Danger 所表明的顏色值變了,只要在 Colors 中修改一下就能夠了。

進一步思考,能夠知道,這裏的 Colors.DangerColors.Info的值具體是什麼並不重要,只要能保證它們彼此不相等就行。好比,咱們寫成下面這樣:

// 這樣定義 `Colors` 依舊不會影響邏輯
const Colors = {
    Danger: 0,
    Info: 1
};
複製代碼

這種定義變量的方式,用 TypeScript 中的枚舉來改寫就是下面這樣的:

// 枚舉變量使用 `enum` 關鍵字定義
// 此處定義了一個枚舉變量 `Colors`
enum Colors {
    Danger,
    Info
}
複製代碼

上面一段代碼通過編譯後,獲得的 JavaScript 源碼以下:

var Colors;
(function (Colors) {
  Colors[Colors["Danger"] = 0] = "Danger";
  Colors[Colors["Info"] = 1] = "Info";
})(Colors || (Colors = {}));
複製代碼

由此可知,

enum Colors {
    Danger,
    Info
}

// 等價於

var Colors = {
    0: 'Danger',
    1: 'Info',
    'Danger': 0,
    'Info': 1
};
複製代碼

咱們修改下初始的例子,使用枚舉來組織邏輯:

enum Colors {
    Danger, // 對應的值是 0
    Info, // 對應的值是 1
    Success // 對應的值是 2
}

// 咱們將函數 `isItDanger` 的參數 `color` 類型約束爲 `Colors`
// 說明此函數只接收 `Colors` 中列舉的值
function isItDanger(color: Colors): boolean {
    return color === Colors.Danger;
}

// 接下來使用 `Colors.Info` 調用 `isItDanger` 函數
isItDanger(Colors.Info); // false
複製代碼

除了使用默認的索引值,咱們還能夠爲枚舉變量中的每一項指定值:

enum Colors {
    Red, // 對應的值是 0
    Blue = 3, // 將 Blue 值指定爲 3
    Green // 接上面的 3,此處的值是 4
}

enum Colors {
    Red = 'red', // 對應的值是 'red'
    Blue = 'blue', // 對應的值是 'blue'
    Green = 'green' // 對應的值是 'green'
}
複製代碼

上篇完。

相關文章
相關標籤/搜索