TypeScript 簡明教程:基本型別(一)

Hopsken發表於2019-03-06

本文為系列文章《TypeScript 簡明教程》中的一篇。

  1. 認識 TypeScript
  2. 安裝 TypeScript
  3. 基本型別(一)

從這篇文章,我們開始講解 TypeScript 的型別系統。之前我們說到,TypeScript 是 JavaScript 的超集,是 JavaScript 的繼承與發展,即所謂的『增強版』。這一點,從 TypeScript 中的基本型別可以看出。TS 的資料型別與 JS 幾乎相同,此外還額外提供一些資料型別方便我們使用。

首先,我們來看 JavaScript 中 6 種原始資料型別在 TypeScript 中的使用,它們分別為:numberstringbooleannullundefinedsymbol

原始資料型別

number 型別

TypeScript 中,使用 number 表示變數為數值型別。與 JavaScript 一致,TypeScript 中所有數值都是浮點數,支援二進位制、八進位制、十進位制和十六進位制四種表示方法。

let decimalNumber: number = 42
let hexNumber: number = 0x2A
let binaryNumber: number = 0b101010
let octalNumber: number = 0o52
複製程式碼

二進位制和八進位制表示採用 ES6 新的寫法,分別使用字首 `0b`(或 `0B`)和 `0o`(或 `0O` 英文字母 O)表示。從 ES5 開始,不再允許使用字首 `0` 表示八進位制。如果 `tsconfig` 中 target 高於 ES5 版本,TypeScript 將會出現提示:禁止使用字首 `0` 表示八進位制。

除了浮點數以外,JavaScript 中還有一組特別的數值:Infinity-InfinityNaN,它們也屬於 number 型別。

const positiveLargestNumber: number = Infinity
const negativeLargestNumber: number = -Infinity

const notANumber: number = NaN
複製程式碼

Infinity 即正無窮,用以表示超出 JS 所能表示的最大值的數值,不同環境下這個值有所不同,可通過 Number.MAX_VALUE 檢視,一般為 1.7976931348623157E+308。與之相對應的是 -Infinity,表示負無窮。

注意區分**負無窮**和**無窮小**兩個概念。負無窮表示比任何數都小的一個負數值,無窮小表示以 0 為極限,無限趨於零的一個正數值。JS 中的無窮小值可通過 `Number.MIN_VALUE` 檢視,一般為 `5E-324`。

NaN 表示一個本應返回數值的運算元未返回數值的情況。比如:

let newNumber: number = Number('a') // => NaN
複製程式碼

更多內容可以參考:ECMAScript 6 入門 - 數值的擴充套件

string 型別

和其他程式語言一樣,字串是必不可少的一種型別,用於表示文字資料。可以通過 string 宣告某個變數為字串型別。

const theAnswer: number = 42
let sentence: string = `The Answer to the Ultimate Question of Life, The Universe, and Everything is ${ theAnswer }.`
複製程式碼

TypeScript 中你依舊可以使用雙引號(")和單引號(')來表示字串。除此以外,你還可以使用 ES6 提供的 模板字串 語法。TypeScript 會自動將其編譯為字串拼接的形式。

// 編譯結果
var theAnswer = 42;
var sentence = "The Answer to the Ultimate Question of Life, The Universe, and Everything is " + theAnswer + ".";
複製程式碼

boolean 型別

布林值可以說是最基本的資料型別了,它的可能取值只有兩個:truefalse。TypeScript 中使用 boolean 表示布林值型別。

let num: number = -10
let isPositive: boolean = num > 0
複製程式碼

symbol 型別

symbol 是 ES6 新增的資料型別,用於表示獨一無二的值,只能 Symbol 函式生成。更多內容可以參考:ECMAScript 6 入門 - Symbol

const sym: symbol = Symbol()
複製程式碼

事實上,TypeScript 官網上並沒有詳細介紹 symbol 這一基礎資料型別。想來原因有二:

  1. symbol 值只能通過 Symbol() 函式生成,由於型別推斷(後面會講)的存在,無需特別宣告變數為 symbol 型別。
  2. symbol 的唯一性依賴於底層實現,不太好做 polyfill,所以對於編譯 target 低於 ES6 版本的情況,並不推薦使用 symbol

不過,出於完整性的考慮,這裡還是簡要提一下。

undefinednull 型別

JavaScript 中,undefinednull 都被用來表示空值。作為兩個原始資料型別,undefined 型別的變數只能被賦值為 undefinednull 型別的變數只能被賦值為 null

// 一般來說,直接定義一個 undefined 或者 null 型別的變數並沒有太大意義
let u: undefined = undefined
let n: null = null
複製程式碼

預設情況下,undefinednull 是所有其他型別的子型別。也就是說,如果 undefinednull 賦值給任何其他型別的變數。

例如:

let person: string = 'Jerry'
person = null // 有效的
複製程式碼

然而,如果在 tsconfig 中開啟了 strictNullChecks,那麼 undefinednull 就只能賦值給 voidany 型別變數以及它們自身型別的變數。強烈建議開啟這一選項,它能幫助避免很多常見的問題

undefinednull 的關係

儘管 undefinednull 都表示空值,但是它們本質上是不同的。undefined 表示變數已宣告但未初始化null 則在邏輯上表示一個空物件指標(這也正是 typeof null === 'object' 的原因)。

無論什麼情況下,都沒有必要把一個變數的值顯式地設定為 undefined,可是同樣的規則對 null 卻不適用。換句話說,只要意在儲存物件的變數還沒有真正儲存物件,就應該明確地讓該變數儲存 null 值。 —— Nicholas C.Zakas 《JavaScript 高階程式設計》

高階型別

void 型別

void 型別表示沒有任何型別,這句話聽起來可能有些自相矛盾。不過,讓我們來考慮這樣一種情況:如果一個函式沒有返回值,我們如何表示其型別?這時候 void 型別就派上用場了。這裡和 C 語言很像。

function warning(): void {
    console.log('WARNING!')
}
複製程式碼

當然,這裡你也可以指定返回值型別為 undefined。因為 JS 中,如果函式沒有返回值,則會預設返回 undefind。不過,使用 void 型別可以使表意更清晰。

然而,宣告一個 void 型別的變數並沒有太大的用處,因為你只能將其賦值為 nullundefined

請注意與 void 運算子區分void 運算子的作用是:對給定的表示式進行求值,然後返回 undefined。如果你從事前端多年,你或許會比較熟悉 javascript:void(0); 這樣的程式碼。感興趣的可以看 這裡

any 型別

某種程度上,any 型別就像是 void 型別的反面,任何型別都是 any 型別的子型別。換句話說,any 型別的變數可以被賦予任何型別的值。

let anything: any = "Ohh, that's crazy"
anything = 42
複製程式碼

對於 any 型別,有兩個需要注意的點。

首先,如果變數在宣告時,沒有宣告其型別,也沒有初始化(因為型別推斷會自動判斷型別),那麼它會被當做 any 型別。

let something   // 等價於 let something: any;
something = 'autochess'
something = 6
複製程式碼

其次,在 any 型別變數上可以訪問任何屬性,即使它並不存在。

let something: any = 42
something.mayExist()    // 沒問題,因為其可能在執行時存在
something.toFixed() // 沒問題,雖然確實存在,但是編譯器並不會去檢查
複製程式碼

事實上,any 型別是 TypeScript 提供的一個『跳出』方案。對於 any 型別變數,編譯器不會做任何型別檢查,直接讓它們通過型別檢查。這在引入第三方庫以及將原有 JS 程式碼改寫為 TS 時尤其有用。

不過,注意不要濫用 any 型別。錯誤地濫用 any 型別會讓 TS 失去其存在的意義。

never 型別

never 用於表示永遠不會存在的值的型別。never 是任何型別的子型別,但沒有型別是 never 的子型別。

對於 never 型別,新手理解起來可能比較困難。但是對於 TypeScript 這樣一個分析程式碼流的語言來說,never 型別是理所當然且必要的。

never 型別常用於兩種情況:

  1. 用於描述從不會有返回值的函式
  2. 用於描述總是丟擲錯誤的函式
// 第一種情況
function neverStop(): never {
    while (true) {
        // do something
    }
}

// 第二種情況
function error(message: string): never {
    throw new Error(message)
}
複製程式碼

對於上述兩種情況,函式的返回型別都是 never

never 型別也可以用做變數的型別註解(例如:let foo: never;),但是這樣做並沒有什麼意義。

下面我們通過一個例子來學習 never 型別的用法:

function checkNumber(x: string | number): boolean {
    if (typeof x === 'number') {
        return true
    } else if (typeof x === 'string') {
        return false
    }
    
    return fail('Failure')
}

function fail(message: string): never {
    throw new Error(message)
}
複製程式碼

對於上面的 checkNumber 函式,如果沒有 return fail('Failure') 這一句的話,TypeScript 會報錯,因為不是所有條件語句都有返回值,當引數 x 既不是 number 也不是 string 時,函式會預設返回 undefined,然而在嚴格模式下,undefined 型別與 boolean 型別並不相容。但是由於 fail 函式返回的 never 型別是 boolean 的子型別,所有上述寫法可以通過編譯。這也是 never 必須是任何型別的子型別的原因。

關於 never 型別,這裡只是舉個例子幫助大家理解。想要真正理解 never 型別,還得結合實戰仔細揣摩才行。

總結

本篇文章主要介紹了 TypeScript 中的原始資料型別和幾個高階型別。通過這篇文章,相信大家已經可以看出 TypeScript 型別系統的強大和完備。

當然這還不是全部,下篇文章我們將介紹 TypeScript 中其他幾個高階型別:ObjectArrayTupleEnum。敬請期待~

相關文章