typescript 高級技巧

用了一段時間的 typescript 以後,深感中大型項目中 typescript 的必要性,它可以提早在編譯期避免許多 bug,如很噁心的拼寫問題。而愈來愈多的 package 也開始使用 ts,學習 ts 已經是勢在必行。html

如下是我在工做中總結到的比較實用的 typescript 技巧。node

本文連接: https://shanyue.tech/post/ts-...ios

01 keyof

keyofObject.keys 略有類似,只不過 keyofinterface 的鍵。git

interface Point {
    x: number;
    y: number;
}

// type keys = "x" | "y"
type keys = keyof Point;

假設有一個 object 以下所示,咱們須要使用 typescript 實現一個 get 函數來獲取它的屬性值github

const data = {
  a: 3,
  hello: 'world'
}

function get(o: object, name: string) {
  return o[name]
}

咱們剛開始可能會這麼寫,不過它有不少缺點typescript

  1. 沒法確認返回類型:這將損失 ts 最大的類型校驗功能
  2. 沒法對 key 作約束:可能會犯拼寫錯誤的問題

這時可使用 keyof 來增強 get 函數的類型功能,有興趣的同窗能夠看看 _.get 的 type 標記以及實現json

function get<T extends object, K extends keyof T>(o: T, name: K): T[K] {
  return o[name]
}

02 Partial & Pick

既然瞭解了 keyof,可使用它對屬性作一些擴展, 如實現 PartialPickPick 通常用在 _.pick安全

type Partial<T> = {
  [P in keyof T]?: T[P];
};

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

interface User {
  id: number;
  age: number;
  name: string;
};

// 至關於: type PartialUser = { id?: number; age?: number; name?: string; }
type PartialUser = Partial<User>

// 至關於: type PickUser = { id: number; age: number; }
type PickUser = Pick<User, "id" | "age">

03 Condition Type

相似於 js 中的 ?: 運算符,可使用它擴展一些基本類型app

T extends U ? X : Y

type isTrue<T> = T extends true ? true : false
// 至關於 type t = false
type t = isTrue<number>

// 至關於 type t = false
type t1 = isTrue<false>

04 never & Exclude & Omit

官方文檔對 never 的描述以下koa

the never type represents the type of values that never occur.

結合 neverconditional type 能夠推出不少有意思並且實用的類型,好比 Omit

type Exclude<T, U> = T extends U ? never : T;

// 至關於: type A = 'a'
type A = Exclude<'x' | 'a', 'x' | 'y' | 'z'>

結合 Exclude 能夠推出 Omit 的寫法

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

interface User {
  id: number;
  age: number;
  name: string;
};

// 至關於: type PickUser = { age: number; name: string; }
type OmitUser = Omit<User, "id">

05 typeof

顧名思義,typeof 表明取某個值的 type,能夠從如下示例來展現他們的用法

const a: number = 3

// 至關於: const b: number = 4
const b: typeof a = 4

在一個典型的服務端項目中,咱們常常須要把一些工具塞到 context 中,如config,logger,db models, utils 等,此時就使用到 typeof

import logger from './logger'
import utils from './utils'

interface Context extends KoaContect {
  logger: typeof logger,
  utils: typeof utils
}

app.use((ctx: Context) => {
  ctx.logger.info('hello, world')

  // 會報錯,由於 logger.ts 中沒有暴露此方法,能夠最大限度的避免拼寫錯誤
  ctx.loger.info('hello, world')
})

06 is

在此以前,先看一個 koa 的錯誤處理流程,如下是對 error 進行集中處理,而且標識 code 的過程

app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    let code = 'BAD_REQUEST'
    if (err.isAxiosError) {
      code = `Axios-${err.code}`
    } else if (err instanceof Sequelize.BaseError) {

    }
    ctx.body = {
      code
    }
  }
})

err.code 處,會編譯出錯,提示 Property 'code' does not exist on type 'Error'.ts(2339)

此時可使用 as AxiosError 或者 as any 來避免報錯,不過強制類型轉換也不夠友好

if ((err as AxiosError).isAxiosError) {
  code = `Axios-${(err as AxiosError).code}`
}

此時可使用 is 來斷定值的類型

function isAxiosError (error: any): error is AxiosError {
  return error.isAxiosError
}

if (isAxiosError(err)) {
  code = `Axios-${err.code}`
}

GraphQL 的源碼中,有不少諸如此類的用法,用以標識類型

export function isType(type: any): type is GraphQLType;

export function isScalarType(type: any): type is GraphQLScalarType;

export function isObjectType(type: any): type is GraphQLObjectType;

export function isInterfaceType(type: any): type is GraphQLInterfaceType;

07 interface & type

interfacetype 的區別是什麼?能夠參考如下 stackoverflow 的問題

https://stackoverflow.com/que...

通常來講,interfacetype 區別很小,好比如下兩種寫法差很少

interface A {
  a: number;
  b: number;
};

type B {
  a: number;
  b: number;
}

其中 interface 能夠以下合併多個,而 type 只能使用 & 類進行鏈接。

interface A {
    a: number;
}

interface A {
    b: number;
}

const a: A = {
    a: 3,
    b: 4
}

08 Dictionary & Many

這幾個語法糖是從 lodash 的 types 源碼中學到的,平時工做中的使用頻率還挺高。

interface Dictionary<T> {
  [index: string]: T;
};

interface NumericDictionary<T> {
  [index: number]: T;
};

const data:Dictionary<number> = {
  a: 3,
  b: 4
}

09 使用 const enum 維護常量表

相比使用字面量對象維護常量,const enum 能夠提供更安全的類型檢查

// 使用 object 維護常量
const enum TODO_STATUS {
    TODO = 'TODO',
    DONE = 'DONE',
    DOING = 'DOING'
}
// 使用 const enum 偉華常量
const enum TODO_STATUS {
    TODO = 'TODO',
    DONE = 'DONE',
    DOING = 'DOING'
}

function todos (status: TODO_STATUS): Todo[];

todos(TODO_STATUS.TODO)

10 VS Code Tips & Typescript Command

使用 VS Code 有時會出現,使用 tsc 編譯時產生的問題與 vs code 提示的問題不一致

找到項目右下角的 Typescript 字樣,右側顯示它的版本號,能夠點擊選擇 Use Workspace Version,它表示與項目依賴的 typescript 版本一直。

或者編輯 .vs-code/settings.json

{
  "typescript.tsdk": "node_modules/typescript/lib"
}

11 Typescript Roadmap

最後一條也是最重要的一條,翻閱 Roadmap,瞭解 ts 的一些新的特性與 bug 修復狀況。

Typescript Roadmap

參考

歡迎關注個人公衆號山月行,在這裏記錄着個人技術成長,歡迎交流

歡迎關注公衆號山月行,在這裏記錄個人技術成長,歡迎交流

相關文章
相關標籤/搜索