使用 TypeScript 開發你的專案

三毛丶發表於2018-04-23

關於 TypeScript

早在去年(2017),TypeScript 贏來了它的爆發式增長。時至今日,隨著 JavaScript 的程式碼數量越來越龐大,越來越多的開發者意識到 JavaScript 在構建大型專案時的不足之處。JavaScript 是動態型別的,只能在 runtime 時進行型別檢查;同時它也給重構大型專案帶來了的困擾,在一定程度上,它是不「易讀」的。而 TypeScript 能夠很好的解決上述問題。

TypeScript 下載量

TypeScript 最早是在 2012 年十月份由微軟開源在 GitHub 上,它是 JavaScript 的一個超集,除了能讓我們使用 ES Future 的各種語法外,還提供如 Enum、Tuple、Generics 等的新語法。當然,向 JavaScript 提供一個可選的靜態型別是一個最重要的變化點了。

在接下來,我將簡單的闡述為什麼靜態型別對大型專案是友好的,以及對 Function type 的一次實踐寫法。

靜態型別對大型專案是友好的

儘早檢查錯誤

如前文所提及的,JavaScript 是動態型別的語言,它沒有 Type System,只能在 runtime 時進行型別檢查,如果你不是足夠的小心,難免會出現下列情況:

使用 TypeScript 開發你的專案

在這個簡單的例子裡,我們認為 someMethod 的引數一個陣列,可是實際情況並不是,它是一個數字。理所當然,它報錯了。

改為 TypeScript 加上簡單的型別推斷時:

使用 TypeScript 開發你的專案

可見,它在編譯前就已經給出了錯誤的提示。

閱讀程式碼友好

或許你也剛好認為「程式碼是給人讀的,只是順便在機器上跑一下」,我相信你會在 Function、Class、Modules 或者其他地方加上 JSDoc。不同於 JSDoc,TypeScript 提供的型別宣告和模組介面形成了文件的形狀,提供程式的行為提示,並在編譯時會校驗程式的正確性。

改動下上個例子:

使用 TypeScript 開發你的專案

當然,對大型專案來說,這可能要複雜的多。儘早的發現錯誤,對閱讀程式碼更友好,或多或少能讓我們在重構專案時更方便。

Function type

先從一個簡單的 Function type 入手:

(arg: number) => string
複製程式碼

在上面的一個 Function type 中,它接收一個數字型別的引數,並返回一個字串型別。

現在來使用它:

const func: (arg: number) => string = String // 在這裡 String 是一個方法
複製程式碼

在實際應用中,並不會這麼用,因為 TypeScript 知道 String 的型別,並能準確的推匯出 func 的型別。

一個更加實際的例子:

function someMethods (callback: (arg: number) => string) {
  return callback(321)
}
複製程式碼

現在,你可以使用這個定義的方法,但是傳入的引數必須符合 (arg: number) => string,比如你可以使用 someMethods(String) 而不能使用 someMethods(Number)

加上函式返回:

function someMethod (
  callback: (num: number) => string
): string {
  const num = 123
  return String(num)
}
複製程式碼

有些時候,並不想傳 callback

function someMethod (
  callback?: (num: number) => string
): string {
  const num = 123
  if (callback) {
    return callback(123)
  }
  return String(num)
}
複製程式碼

或者,我們希望 callback 是必須的,如果你不想提供一個函式,你必須顯式的提供一個 null:

function someMethod (
  callback: null | ((num: number) => string)
): string {
  const num = 123
  if (callback) {
    return callback(123)
  }
  return String(num)
}
複製程式碼

定義成一個 type:

type SomeMethod = (callback?: ((num: number) => string)) => string
複製程式碼

在這個 type 裡,我們定義了一個 someMethod 方法,它有一個可選引數 callback,同時規定這個 callback 有且僅有一個型別為 number 的引數。

接下來,我們擴充套件這個 type ,使用泛型(你可以簡單的理解泛型是一種資料型別)並改變它的 callback:

type SomeMethod<T> = (
  callback: (value: T, index: number, array: T[]) => T
) => T
複製程式碼

給泛型加一個預設值,並加個可選引數:

type SomeMethod<T = string> = (
  callback: (value: T, index: number, array: T[]) => T,
  thisArg?: T
) => T
複製程式碼

至此,一個簡單的 Function type 已經完成了。

事實上,Function types 還有各種有趣玩法:

type SomeMethod<T = string> = (
  callback: (...arg: (T[] | T)[]) => T[]
  // ...
) => T
複製程式碼
// 多泛型,並帶有約束時
type OthemMethod <T, P extends keyof T> = (
  obj: T,
  key: P
) => T[P]
複製程式碼

實際上 TypeScript 2.4 版本以後,可以對函式呼叫的返回值進行判斷

function arrayMap<T, U>(
  f: (x: T) => U
): (a: T[]) => U[] {
  return a => a.map(f)
}

const lengths: (a: string[]) => number[] = arrayMap(a => a.length)

console.log(lengths(['123', '1', '1'])) // 3, 1, 1
複製程式碼

以及更多有趣的寫法,這裡不再介紹了。


參考:

  • https://www.typescriptlang.org/docs/home.html
  • https://zhuanlan.zhihu.com/p/24267683
  • https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-4.html
  • https://juejin.im/post/59c46bc86fb9a00a4636f939

相關文章