🍩はじめに
プログラム書いてるときに「この変数は〇〇なのに TypeScript さんがいい加減に推論してくれない……」ってなることがある。
null
じゃないのは分かってるのにとか、Union
型の一部なのは分かってるのにとか。
そういうときは any
型にしたり as
で強制型変換したりで誤魔化すことが多いんだけど、それらよりはユーザー定義の型ガード関数を使っていこうという話。
使用上の注意
型ガード関数の正しさは TypeScript さんが保証してくれないため、間違ったコードで型を簡単に偽装できてしまう。 |
App | Version |
---|---|
TypeScript |
4.9.5 |
🏰型ガード関数(type guard function)
ユーザー定義の型ガード関数とも呼ばれる、戻り値の型指定が x is T
になっている[1]やつ。
実際に返す値は boolean 値であり、if
文の条件式に使って型を絞り込む。
// type Hoge = { hoge: string }
function isHoge(value: unknown): value is Hoge {
if (typeof value !== 'object' || value == null) {
return false
}
return 'hoge' in value && typeof value.hoge === 'string'
}
const test1 = (x: unknown) => {
if (isHoge(x)) {
// Hoge 型に絞り込めたのでプロパティにアクセスできる
console.log(x.hoge)
} else {
console.warn(`x is not Hoge.`)
}
}
test1({ hoge: 'hogehoge' }) // -> 'hogehoge'
test1({ hoge: 'success', foo: 'not display' }) // -> 'success'
test1(12) // -> x is not Hoge.
test1({ bar: 'bar' }) // -> x is not Hoge.
unknown 型
|
🚨アサーション関数(assertion function)
戻り値の型指定が asserts x is T
の形になっているやつ。
上記の型ガード関数とは違い、例外が発生するか否かで判定される。
正常終了すれば以降のコードブロック内において型が絞り込まれる。
例外が発生するため、もともとエラーハンドリングを行っているコードやテストコード内でよく使われる。
// type Hoge = { hoge: string }
function assertHoge(value: unknown): asserts value is Hoge {
if (typeof value !== 'object' || value == null) {
throw new Error(`"${value}" is not Hoge type.`)
}
if (!('hoge' in value && typeof value.hoge === 'string')) {
throw new Error(`"${value}" does not have a hoge property.`)
}
}
const test2 = (x: unknown) => {
assertHoge(x)
// Hoge 型に絞り込めたのでプロパティにアクセスできる
console.log(x.hoge)
}
test2({ hoge: 'hogehoge' }) // -> 'hogehoge'
test2({ hoge: 'success', foo: 'not display' }) // -> 'success'
test2(12) // -> Error!
test2({ bar: 'bar' }) // -> Error!
assert モジュール
本番環境に向けては unassert で |
🛠️トラブルシューティング
Assertions require every name in the call target to be declared with an explicit type annotation.
アサーション関数をアロー関数で定義してしまっているのが原因。
詳細は以下の記事通り。
📘 この直し方→Assertions require every name in the call target to be declared with an explicit type annotation. - Qiita