🔐
TypeScriptで安全なユーザー定義型ガードを作る方法を考えた
ユーザー定義型ガード(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 R
のR
を推論させています。
実装を元にしてユーザー定義型ガードの型が決まるので、実装と型の乖離がなくなります。
よろしければ使ってみてください。
Discussion