この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
みなさん 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をさっそく使ってみた