ロゴ
clock2019.12.16

tsdでTypeScriptの型定義とテストで戦う

DEV

この記事はTypeScript Advent Calendar 2019 の 16日目の記事です。

「Mapped types」,「Conditional Types」などTypeScriptの型システムは高機能です。 しかし、高機能がゆえに複雑で直感的には分かりくい型を生み出していることも事実です。

TypeScriptの型定義は型を返す関数として扱うことができ、型がバグを生み出す原因にもなります。 そんな型でのバグをなくすために型のユニットテストを紹介します。

tsd

型をテストするためにtsdというライブラリを使用します。

tsdはexpectType,expectErrorなどの様々なアサーションで型定義をチェックすることができます。

テスト

今回はjestを使ってテストを行うのでtsdとjestに関連するライブラリを用意します。

bash
yarn add -D tsd jest ts-jest @types/jest

package.json

次にpackage.jsonを編集します。

jestの設定を書いていきます。

package.json
{
  "name": "tsd-unit-testing",
  "scripts": {
    "test": "jest",
  },
  "jest": {
    "preset": "ts-jest",
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js"
    ],
    "testMatch": [
      "**/test-dts/*.(ts|tsx|js)"
    ]
  },
  "devDependencies": {
    "@types/jest": "^24.0.23",
    "jest": "24.9.0",
    "ts-jest": "24.2.0",
    "tsd": "0.11.0",
    "typescript": "^3.7.3",
  },
}

型定義

次に今回テストする型定義を準備します。

「TがUに含まれていればTを、そうでなければunknown」を返すConditional Typesです

types.ts
export type Condition<T, U> = T extends U ? T : unknown; 

テストを書く

実際にテストを書いていきます。

expectTypeはジェネリクスで指定した型が引数で指定した値と同じかを検査します。 以下の例では'kuma'型は'inu' | 'neko'型のUnion Typeには存在しないのでunknown型が返ってくるためテストは通ります🎉

test-dts/type.test-d.ts
import { expectType } from 'tsd';
import { Condition } from './types'

describe('Check type definitions', () => {
  it('Condition', () => {
    let animal: Condition<'kuma', 'inu' | 'neko'>

    expectType<unknown>(animal)
  })
});
yarn test

$ jest
PASS  test-dts/type.test-d.ts
  Check type definitions
    ✓ Condition (1ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.715s
Ran all test suites.
✨  Done in 7.07s.

おわりに

他にもいくつかアサーションが用意されているので気になる方はドキュメントをどうぞ!

普通に書いていると高度で複雑な型はあまり出てこないですが、ライブラリを作る際には活用できるかもしれません。(vue-nextで使っているのを観測しました)

余談

昔、tsdというTypeSciptの型定義管理ツールがあったらしいですね...

\ SHARE /

プロフィール画像
くま

フロントエンドエンジニア👨‍💻サーバサイドもちょっと書けるよ

イケてるエンジニア目指してるよ


最近は昆布と旅行雑誌🏄‍♀️に絶賛ハマり中