我們使用開發TypeScript專案的時候或多或少都要用到第三方庫。一般常用的第三方庫都有對應的型別檔案,或者可以新增型別包。但是如果這個第三方庫沒有對應的型別檔案可以用就會稍有麻煩。今天就來探討一下這些都是怎麼處理的。
第一種:第三方庫是TypeScript開發的
這是最好的情況,自帶型別。使用TypeScript開發的庫在發到npm之前就編譯為JS了,這樣可以直接在瀏覽器和node環境下執行。同時,在內部使用呢的型別也一起打包釋出到npm了。這些型別在index.d.ts
檔案,最終在node_modules下的專案目錄裡。
比如,react-content-loader
是用TypeScript開發的,最終它的型別檔案在node_modules/react-content-loader目錄下。
這樣的開發方式還有一個好處,那就是這些型別會和程式碼的變動同步。因為型別都是從實際的程式碼中編譯得來的。
可惜並不是所有專案都是TypeScript開發的。遇到這樣的庫很多的好處都沒有了,比如智慧提示。。。那麼應該怎麼辦呢。
第二種:第三方庫有@types型別檔案
很多第三方庫不是用TypeScript開發的。這種情況可以使用DefinitelyTyped
的方式類實現。有一個github庫:DefnitelyTyped。這裡就是用來儲存型別檔案的。所以第三方JS庫的型別檔案可以放在這裡。比如React,可以用@types/react
。styled-component
可以用@types/styled-component
。
這些型別都是社群維護的,也就是說使用這些庫的人在維護這些庫的型別。雖然不是完全的不可控,還是有人會review這些型別檔案。但是,難免的是這些型別不能和庫一起演進,進而出現型別和庫不匹配的情況。畢竟庫本身並不支援型別。
第三種:第三方庫沒有型別檔案,也不是TypeScript開發的
TypeScript新建自己的型別非常簡單。TypeScript預設的從node_modules目錄的@types
子目錄,還有index.d.ts檔案獲取型別。也就是說你可以在你專案的任意位置建一個index.d.ts檔案,新增型別。也可以在types目錄下維護型別。
在index.d.ts檔案裡,你可以定義你專案裡的所有型別。記得把型別定義在特定的模組或者namespace下。比如:
declare module 'babel-plugin-relay/macro' {
export { graphql as default } from 'react-relay';
}
因為這個庫要這麼用:
import graphql from 'babel-plugin-relay/macro';
monkeypatch
有時候在DefinitelyType
,也就是@types/some-lib的型別是錯的。要搞定這個錯誤就只能用monkeypatch了。其實這樣也不是一個好主意,最好是給DefinitlyType提一個pr。但是這樣沒那麼快生效。所以只能走monkeypatch。
開頭也是一樣,新建一個index.d.ts檔案來存放某個庫的型別。在這個檔案裡宣告你要處理的模組或者namespace。然後在這個模組裡新的型別。然後,你要覆蓋掉錯誤的型別,你要先import以前的型別。比如現在有@testing-library/react-hooks
。
// inside types/testing-library__react-hooks/index.d.ts
import "@testing-library/react-hooks"
declare module "@testing-library/react-hooks" {
interface RenderHookResult {
// This function is not included in the typings as of 3.2.1
wait(
callback: (...args: any[]) => any,
options?: { timeout?: number; suppressErrors?: boolean },
): Promise<void>
}
}
現在@testing-library/react-hooks不僅包含了原始的型別,也包含了新方法的型別定義。
最後
第三方型別非常重要,本文也說明了我們專案裡的型別是如何處理的。一個穩定,與時俱進的型別是選擇第三方庫的重要指標,因為準確的型別可以極大的幫助開發。如果一個庫沒有型別定義,TypeScript也一樣提供了一種機制來處理。