從 JavaScript 到 TypeScript - 聲明類型

gy680871 8年前發布 | 26K 次閱讀 JavaScript開發 TypeScript JavaScript

從 JavaScruot 語法改寫為 TypeScript 語法,有兩個關鍵點,一點是類成員變量(Field)需要聲明,另一點是要為各種東西(變量、參數、函數/方法等)聲明類型。

從 ES6 語法改寫為 TypeScript 語法,有兩個比較重要的知識點,一個是聲明類成員,另一個是聲明類型。這兩個語法特點在 JavaScript 中都不存在。而這兩個點直接引出了兩個關鍵性的問題,有哪些類型?怎樣聲明?

類型

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

  • undefined

  • function

  • boolean

  • number

  • string

  • object

  • symbol

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

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

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

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

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

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

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

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

  • any ,代表任意類型

  • void ,表示沒有類型,用于聲明函數類型

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

  • ……

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

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

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

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

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

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

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

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

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

聲明類型

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

// # typescript

// 聲明函數 pow 是 number 類型,即返回值是 number 類型
// 聲明參數 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));
    }
}

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

// # javascript

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

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

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

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 到底確實是你想要添加的屬性名稱呢,還是你不小心寫錯了的呢?

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

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

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

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

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

小結

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

  1. 類成員需要聲明。

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

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

 

來自:https://segmentfault.com/a/1190000009653948

 

 本文由用戶 gy680871 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!