🔐

TypeScriptで安全なユーザー定義型ガードを作る方法を考えた

2022/11/21に公開

ユーザー定義型ガード(user-defined type guards)を使うと次のように自分で好きなように型ガードを定義することができます。

function isString(v: unknown): v is string {
  return typeof v === 'string'
}

一方で、コードに誤りがあったとしてもエラーにならないという問題があります。
そこで、型推論を活用して、型と実装の相違が生まれないような仕組みを考えてみました。
簡単な仕組みなので、既出でしたらすみません。

やり方

まず、smartTypeGuardという関数を定義します。

function smartTypeGuard<T, R extends T>(
    f: (value: T) => {value: R} | undefined | false):
    (value: T) => value is R {
  return (value: T): value is R => Boolean(f(value))
}

そちらを次のように使います。

const a = [10, 'foo', undefined, 'bar']
const b = a.filter(smartTypeGuard((value) =>
  (typeof value === 'number' || typeof value === 'string') && {value}))

aの型はArray<string | number | undefined>ですが、bの型はArray<string | number>になっています。
また、bの定義を次のように書き換えた場合、bの型はArray<string>になり安全です。

const b = a.filter(smartTypeGuard((value) =>
  typeof value === 'string' && {value}))

解説

解説するというほどでもありませんが、一応簡単に。

smartTypeGuardの引数に渡した関数では、TypeScriptの型ガードで絞り込まれた値を{value}として返すことで、value is RRを推論させています。
実装を元にしてユーザー定義型ガードの型が決まるので、実装と型の乖離がなくなります。


よろしければ使ってみてください。

Discussion