從 JavaScript 到 TypeScript

2018-06-09 10:30 更新

從 JavaScript 語法改寫為 TypeScript 語法,有兩個關鍵點,一點是類成員變量(Field)需要聲明,另一點是要為各種東西(變量、參數(shù)、函數(shù)/方法等)聲明類型。而這兩個點直接引出了兩個關鍵性的問題,有哪些類型?怎樣聲明?

類型

在說 TypeScript 的類型之前,我們先復習一下 JavaScript 的七種類型:

  • undefined

  • function

  • boolean

  • number

  • string

  • object

  • symbol

這七種類型都是可以通過 typeof 運算符算出來的,但其中并沒有我們常見的 Arraynull,Date 之類的類型——因為它們其實都是 object。

TypeScript 的重要特性之一就是類型,所以 TypeScript 中的類型要講究得多,除了 JavaScript 中的類型之外,還定義了其它一些(不完全列表)

  • Array<T>,或 T[],表示 T 類型的數(shù)組

  • null,空類型,其作用與 strictNullChecks 編譯參數(shù)有關

  • Tuple(元組),形如 [Number, String]

  • enum T,定義枚舉類型 T,可理解為集中對數(shù)值常量進行命名

  • interface T,接口,T 是一種接口類型

  • class T,類,T 是一種類型

  • any,代表任意類型

  • void,表示沒有類型,用于聲明函數(shù)類型

  • never,表示函數(shù)不可返回的神奇類型

  • ……

具體的類型這里就不詳述了,官方 Handbook 的 Basic Type、Interfaces、Classes、Enum、Advanced Types 這幾部分說得非常清楚。

不過仍然有一種類型相關的特性不得不提——泛型。如果只是說數(shù)據(jù)類型,純粹的 JSer 們還可以理解,畢竟類型不是新鮮玩意兒,只是擴展了點種類。但是泛型這個東西,純粹的 JSer 們可能就沒啥概念了。

泛型主要是用一個符號來表示一些類型,只要是符合約束條件(默認無約束)的類型,都可以替換掉這個類型符號來使用,比如

function test<T>(v: T) {
    console.log(v);
}

test<boolean>(true);    // 顯式指定 T 由 boolean 替代
test("hello");          // 推斷(隱式) T 被 string 替代
test(123);              // 推斷(隱式) T 被 number 替代

泛型與強類型相關,即需要進行嚴格的類型檢查,又想少寫相似代碼,所以干脆用某個符號來代替類型。泛型這個名稱本身可能并不是很好理解,但是如果借用 C++ 的“模板”概念,就好理解了。比如上面的泛型函數(shù),根據(jù)后面的調(diào)用,可以被解釋為三個函數(shù),相當于套用模板,用實際類型代替了 T

function test(v: boolean) { ... }
function test(v: string) { ... }
function test(v: number) { ... }

關于泛型,更詳細的內(nèi)容可以參考 Handbook 的 Generic 部分。

類型就簡述到這里,簡單的類型一看就能明白,高級一點的類型我們以后再開專題來詳述。不過既然選擇使用 TypeScript,必然會用到它的靜態(tài)類型特性,那就必須強化識別類型的意識,并養(yǎng)成這樣的習慣。對于純 JSer 來說,這是一個巨大的挑戰(zhàn)。

聲明類型

聲明類型,主要是指聲明變量/常量,函數(shù)/方法和類成員的類型。JS 中使用 var 聲明一個變量,ES6 擴展了 let 和 const。這幾種聲明 TypeScript 都支持。要為變量或者常量指定類型也很簡單,就是在變量/常量名后面加個冒號,再指定類型即可,比如

// # typescript

// 聲明函數(shù) pow 是 number 類型,即返回值是 number 類型
// 聲明參數(shù) n 是 number 類型
function pow(n: number): number {
    return n * n;
}

// 聲明 test 是無返回值的
function test(): void {
    for (let i: number = 0; i < 10; i++) {  // 聲明 i 是 number
        console.log(pow(i));
    }
}

這段代碼演示了對函數(shù)類型、參數(shù)類型和變量類型地聲明。這相對于 JavaScript 代碼來說,似乎變得更復雜了。但是考慮下,如果我們在某處不小心這樣調(diào)用了 pow

// # javascript

let n = "a";
let r = pow(n);     // 這里存在一個潛在的錯誤

JavaScript 不會提前檢查錯誤的,只有在執(zhí)行到 r = pow(n) 的時候給 r 賦值為 NaN。然后如果別處又用到 r,可能就會造成連鎖錯誤,可能很要調(diào)試一陣才把問題找得出來。

不過上面兩行代碼在 TypeScript 里是通不過轉(zhuǎn)譯的,它會報告一個類型不匹配的錯誤:

Argument of type 'string' is not assignable to parameter of type 'number'.

聲明類成員

這時先來看一段 JavaScript 代碼

// # javascript (es6)

class Person {
    constructor(name) {
        this._name = name;
    }

    get name() {
        return this._name;
    }
}

這段 JavaScript 代碼如果翻譯成 TypeScript 代碼,會是這樣

// # typescript

class Person {
    private _name: string;

    public constructor(name: string) {
        this._name = name;
    }

    public get name(): string {
        return this._name;
    }
}

注意到 private _name: string,這句話是在聲明類成員變量 _name。JavaScript 里是不需要聲明的,對 this._name 賦值,它自然就有了,但在 TypeScript 里如果不聲明,就會報告屬性不存在的錯誤:

Property '_name' does not exist on type 'Person'.

雖然寫起來麻煩了一點,但是我也能理解 TypeScript 的苦衷。如果沒有這些聲明,tsc 就搞不清楚你在使用 obj.xxxx 或者 this.xxxx 的時候,這個 xxxx 到底確實是你想要添加的屬性名稱呢,還是你不小心寫錯了的呢?

另外要注意到的是 privatepublic 修飾符。JavaScript 中存在私有成員,為了實現(xiàn)私有,大家都想了不少辦法,比如閉包。

TypeScript 提供了 private 來修飾私有成員,protected 修改保護(子類可用)成員,public 修飾公共成員。如果不添加修飾符,默認作為 public,以兼容 JavaScript 的類成員定義。不過特別需要注意的是,這些修飾符只在 TypeScript 環(huán)境(比如轉(zhuǎn)譯過程)有效,轉(zhuǎn)譯成 JavaScript 之后,仍然所有成員都是公共訪問權限的。比如上例中的 TypeScript 代碼轉(zhuǎn)譯出來基本上就是之前的 JavaScript 代碼,其 _name 屬性在外部仍可訪問。

當然在 TypeScript 代碼中,如果外部訪問了 _name,tsc 是會報告錯誤的

Property '_name' is private and only accessible within class 'Person'.

所以應用內(nèi)使用 private 完全沒問題,但是如果你寫的東西需要做為第三方庫發(fā)布,那就要想一些手段來進行“私有化”了,其手段和 JavaScript 并沒什么不同。

小結(jié)

從 JavaScript 語法改寫 TypeScript 語法,我們來做個簡單的總結(jié):

  1. 類成員需要聲明。

  2. 變量、函數(shù)參數(shù)和返回值需要申明類型。

如果所有這些東西都要聲明類型,工作量還是滿大的,所以我建議:就接口部分聲明類型。也就是說,類成員、函數(shù)/方法的參數(shù)和返回類型要聲明類型,便于編輯器進行語法提示,局部使用的變量或者箭頭函數(shù),在能明確推導出其類型的時候,可以不聲明類型。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號