banner
沈青川

旧巷馆子

愿我如长风,渡君行万里。
twitter
jike

Array.filter を使用する際のカスタム型アサーションの巧妙な活用

もし 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メソッドに型引数を渡すことができるようですし、または.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)

最終的な結果は非常に簡潔で直感的で読みやすいものになります。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。