TL;DR

  • 宣言のマージを使うと、既存の API の型を改善できる。any を返す API を unknown を返すように直したり、問題のある使い方を禁止したりできる。
  • 実行時の動作は変わらないので、この 2 つの用途以外ではなるべく使わない。

内容

any を返す API を改善する

宣言のマージを使うと、既存の API の型を改善できる(項目13 type(型エイリアス)とinterfaceの違いを知る)。たとえば JSON.parse は any を返すが、次のように書けば unknown を返すようになる(項目46 型が不明な値には、anyではなくunknownを使う)。

// safe-json.d.ts
interface JSON {
  parse(text: string, reviver?: (this: any, key: string, value: any) => any): unknown
}
declare let apiResponse: string
const response = JSON.parse(apiResponse)
//    ^? const response: unknown

これで、使うときには型アサーションが必須になる。

const cacheExpirationTime = response.lastModified + 3600
//                          ~~~~~~~~ 'response' is of type 'unknown'.
interface ApiResponse {
  lastModified: number
}
 
const response = JSON.parse(apiResponse) as ApiResponse
const cacheExpirationTime = response.lastModified + 3600

Fetch API の Response.prototype.json() も any を返すが、JSON.parse と同じように直せる。

interface Body {
  json(): Promise<unknown>
}

問題のある使い方を禁止する

問題のある機能の使用を禁止することもできる。たとえば Set のコンストラクタに string を渡すと、'abc' は Set (3) { 'a', 'b', 'c' } という意図しない値になる。これを防ぐには、string を渡したときに void を返すようにすればよい。すると、コンストラクタの戻り値を使おうとしたときに型エラーが出る。

const s = new Set("abc")
//    ^? const s: void
console.log(s.has("abc"))
//            ~~~ Property 'has' does not exist on type 'void'.

何が起きているのかをより明確に伝えたいなら、エラーメッセージを戻り値の型にし、@deprecated を付けるとよい。

interface SetConstructor {
  /** @deprecated */
  new (str: string): "Error! new Set(string) is banned"
}

本来は new した時点で型エラーにしたいが、今の TypeScript ではできない。

使うときの注意

モジュールオーグメンテーションは、実行時の動作には影響しない。実行時の挙動を反映しない型を足すと読み手を混乱させるおそれがあるので、上の 2 つの用途以外ではなるべく使わない(項目40 正確でない型より精度の低い型を選択する)。また、すでに void を返す関数やメソッドの使用は、この方法では禁止できない。

キーワード