微信小程式 TypeScript 嘗試

Nemocdz發表於2019-02-07

自從去年開始在專案裡寫了一段時間 Javascript 後,感覺沒有型別檢查的語言還是不太適合我,所以一直想嘗試下 TypeScript,然而由於專案龐大,人員協作問題,一時半會沒辦法切成 TypeScript。正好最近有小程式的需求和小程式去年 11 月開始官方支援了 TypeScript,所以拿來練練手。

Why TypeScript?

大概是我這半年寫的 Swift 比較多,而 Swift 中的靜態型別和協議是我很喜歡的特性。正好 TypeScript 為 JavaScript 帶來了靜態型別介面

可選的靜態型別

“動態型別一時爽,程式碼重構火葬場“,對於我這種極度喜歡重(xia)構(gai)程式碼的人來說,JavaScript 毫無型別提示,型別全靠命名猜測是極度不友好的。而 TypeScript 加上了靈活的型別系統,不僅可以編碼期檢查,還能增強程式碼的可讀性,並提供了 any 型別進行緩衝。

介面

介面和協議,只是不一樣的叫法而已,Java、C#、TypeScript 叫 Interface,Swift、Kotlin 叫 Protocol,就是一種規則宣告。專案中,和後端介面資料互動,頁面傳遞資料,資料持有,方法代理的地方,有了介面就會更加方便,易重構。TypeScript 的 Interface + JavaScript 簡單的物件就讓資料構建變得簡單又不容易出錯。

小程式對 TypeScript 的支援

TypeScript 有一個很重要的東西,就是 d.ts 檔案。d.ts 檔案其實相當於 C 系語言裡面的 .h 標頭檔案,宣告瞭對外暴露的方法和屬性。而小程式官方對 TypeScript 的支援,意味著官方會維護小程式本身 API 的 d.ts 檔案,也就是 typing 庫,這樣當 API 發生變動時,就可以即時變更。

使用也很簡單,更新微信開發者工具到最新版,在建立新專案時選擇 TypeScript 模板。

螢幕快照 2019-01-30 下午8.51.40

建立後,我們可以看到專案裡帶上了 typings 庫,以及 TypeScript 的配置檔案 tsconifg。之後,儲存時就不會自動編譯了,要點選小程式工具欄的編譯按鈕才可以。

這裡有一個坑,筆者電腦安裝的 TypeScript 版本是 3.2.2 版本。編譯時會被找不到全域性型別 CallableFunction 和 NewableFunction。

image-20190130195121996

解決方法也很簡單,到 node_modules 路徑下的 TypeScript 包的 bin 目錄下,lib.es5.d.ts 檔案裡面把這兩個型別的 Interface 拷貝到,小程式 typing 目錄下的 lib.wa.es6.d.ts 裡面就可以了。小程式模板裡這個檔案應該是拷貝 TypeScript 官方的,但沒有隨著官方升級而改變。

事件

檢視的事件,對應的型別筆者在 typings 中並沒有看到有 Interface 定義,所以只能暫時用 any,然後自己再用

as 轉一下 event 攜帶的資料的型別。

Page&Data

每個 Page 物件,在 typing 裡是這麼定義的。

declare const Page: Page.PageConstructor

interface PageConstructor {
    <D extends IAnyObject, T extends IAnyObject & PageInstance>(
      options: PageInstance<D, T> & T
    ): void
  }
複製程式碼

也就是說,它支援 D 和 T 兩個範型。這兩個範型是什麼呢?小程式裡,Page 是這麼寫的。

Page({});
複製程式碼

也就是說,options 引數就是一個 PageInstance,範型也被傳入了。

interface PageInstance<D extends IAnyObject = any, T extends IAnyObject = any> extends PageInstanceBaseProps<D>
複製程式碼

PageInstance 裡面定義了 Page 宣告週期的方法,而且繼承自 PageInstanceBaseProps,並將範型 D 傳入。

interface PageInstanceBaseProps<D extends IAnyObject = any> {
    data?: D
    //...
}
複製程式碼

所以這個 D 範型,其實就是 data 的型別介面。因為 data 不是必須實現的,所以這裡是可選型 ?。

那麼 T 是什麼呢?

T extends IAnyObject & PageInstance
複製程式碼

T 其實就是對 PageInstance 的擴充,PageInstance 是 Page 的例項介面,那麼 T 其實就是在 Page 裡面 this 的型別介面了,也就是說,需要在 Page 裡新增的方法和屬性,都在 T 裡定義。

所以,對於一個普通頁面我們可以宣告兩個介面,一個代表 data, 一個代表 page,舉個例子。

interface IIntroPage {
	nextButtonTap(event: any): void;
    isLoading: boolean;
}

interface IIntroData {
    test: string;
}

Page<IIntroData, IIntroPage>({
    isLoading: false,
	nextButtonTap(event: any) {
        this.isLoading = true;
	}
});
複製程式碼

如果這個頁面不需要 data 或者不需要擴充套件 page,用 IAnyObject 代替 D 或者 T 即可。

interface PageInstanceBaseProps<D extends IAnyObject = any> {
    data?: D
    setData?<K extends keyof D>(
      data: D | Pick<D, K> | IAnyObject,
      callback?: () => void
    ): void
}
複製程式碼

同時,由於 setData 和 data 都被宣告為可選項,使用時需要加上!,this.setData!({})this.data!

其他就沒什麼了,用上 TypeScript 之後,官方的 API 都可以直接看引數和返回值的型別,再也不用去查文件猜測型別了。

呼叫 JavaScript

為 JavaScript 編寫一個簡單的 .d.ts 檔案,將需要呼叫的類和方法暴露出來。詳情見如何編寫一個d.ts檔案

最後

雖然筆者用了 TypeScript 不久,但嚴格的檢查的確讓我在增刪改介面欄位能快速全域性重構,而且方法呼叫聯想,API 檢視也方便了不少。小程式對 TypeScript 的支援日常使用開發是沒有什麼問題了,就是官方的文件指引比較少。

相關文章