もし TypeScript のユーザーであるなら、以下のようなシナリオに遭遇したことがあるかもしれません:
interface Base {
type: 't1' | 't2'
}
interface Extend1 extends Base {
ccc: string
bbb: number
}
interface Extend2 extends Base {
aaa: boolean
}
配列があり、その型の一部のフィールドのみが確定しており、各要素を反復処理する際に操作する必要があります。要素の実際の型を特定のカスタムロジックによって判断することしかできません。
そして、結果を一連の呼び出しで取得したいが、同時に特定のサブタイプの要素をフィルタリングしたい場合、次のようなコードを書くことがよくあります:
const arr: Base[] = [/* ... */]
const arrOfExt1 = arr
.filter(item => item.type === 't1') as Extend1[]
上記のような使用方法は非常に主観的に見えますが、もちろんこのように使用することはできますが、「フィルタ後のアサーション」に関してはより良い解決策があります。
問題の核心は、.filter
の戻り値がまだBase[]
のように見えることです。そして、.filter
メソッドの TS の組み込み型定義を開くと:
えっ、もう一つ方法がある?.filter
メソッドに型引数を渡すことができるようですし、または.filter
に渡される引数関数のシグネチャ部分で明示的に型述語を定義することもできます。TypeScript の「型述語」は、カスタム型アサーションを定義するために使用できるため、次のようにすることができます:
const arrOfExt1 = arr
.filter((item): item is Extend1 => item.type === 't1')
console.log(arr_of_ext1)
// ?^ Extend1[]
コードに従って、.filter
を使用する側はビジネスロジックに属しており、渡される predicate 関数は非常に独立したユーティリティです。この型アサーション関数は複数回再利用される可能性があるため、判断関数を抽出することを検討してください:
function isExtend1(item: Base): item is Extend1 {
return item.type === 't1'
}
const arrOfExt1 = arr.filter(isExtend1)
最終的な結果は非常に簡潔で直感的で読みやすいものになります。