TypeScript 3.7 で追加された Assertion Functions を使って null チェックを楽にする
みなさん null チェックしてますか ? ぼくは今日も元気に null チェックしています。例えば渡された id で配列を走査してそのプロパティを返したいとき、ありますよね。次のようなコードです。
interface Item { id: number name: string } function getName(items: Array<Item>, id: number) { const target = items.find(item => item.id === id) return target.name // error }
ただし、このままでは TypeScript によってエラーが出力されてしまいます。Array.prototype.find
は undefined
が返ってくる可能性があるからです。 このエラーをなくすために null チェックしましょう。
function getName(items: Array<Item>, id: number) { const target = items.find(item => item.id === id) if (target == null) { throw new Error('this path must not be reached') } return target.name }
値が null
だった場合は例外を飛ばしてその後のコードが実行されないようにしています。こうすると TypeScript コンパイラーは null でない型に確定したとして、エラーを出さなくなります。例外メッセージで表現していますが、存在しない id が渡されないことを関数外部で保証している想定です、少なくとも production 環境では。
さて、 null チェックをするにあたっていちいち if
文を書いて、例外を定義して、というのは非常につらみがあります。なにより長い。コードの見通しが悪くなってしまいます。
この課題は TypeScript 3.7 で追加された Assertion Functions を使うことによって解決されます。
function assertIsDefined<T>(val: T): asserts val is NonNullable<T> { if (val === undefined || val === null) { throw new Error( `Expected 'val' to be defined, but received ${val}` ); } } function getName(items: Array<Item>, id: number) { const target = items.find(item => item.id === id) assertIsDefined(target) return target.name }
assertIsDefined
関数を定義し、 null チェックの部分で使っています。ポイントは 2 つ。
- 返り値の型が
asserts val is NonNullable<T>
になっている。 - 条件に違反した場合、例外を飛ばしている
assertIsDefined
関数から何かしら値が返った場合、つまり何も例外が飛ばなかった場合、 TypeScript は引数として渡されたものを is
の後ろに書いた型に確定させるという挙動をします。ここでは引数に渡されたものが null もしくは undefined でない場合、 NonNullable<T>
に確定させてくれます。 if
文を用いたコードに比べると読みやすいですね。
例として assertIsDefined
を挙げましたがこれは TypeScript 公式ドキュメントに書かれているものです。他にこれを汎用化した assert
関数も紹介されています。
function assert(condition: any, msg?: string): asserts condition { if (!condition) { throw new AssertionError(msg) } }
Node.js で定義されている assert
と同様です。が、 condition
に指定する式を間違えるとバグを生みますので、使用には注意しましょう。
function a(src: any) { assert(typeof src === 'string') src.toUpperCase() } a(42) // no errors!!
こういった悲劇を防ぐために、 assertIsDefined
のようにできるだけ用途を限定した方が良いでしょう。
TypeScript 3.7 は他にも色々便利なものが追加されています。上手に使っていきましょう。
TypeScript 3.7.2がリリースされました!Optional ChainingやNullish Coalescingをさっそく使ってみた