Clever use of custom type assertions in Array.filter

If you are a TypeScript user, you may have experienced the following scenario:

interface Base {
  type: 't1' | 't2'
interface Extend1 extends Base {
  ccc: string
  bbb: number
interface Extend2 extends Base {
  aaa: boolean

You have an array where only a part of the fields are determined, and you need to operate on each element during traversal, only able to determine the actual type of the element through some custom logic.

And when you want to get the result with just a single call, while also wanting to filter out elements of a certain subtype, you often end up writing code like this:

const arr: Base[] = [/* ... */]
const arrOfExt1 = arr
  .filter(item => item.type === 't1') as Extend1[]

The above usage of assertion may seem subjective, and it is fine to use it this way, but there is a better solution for the "assertion after filter" issue.

The core issue lies in the fact that the return value of .filter seems to still be just a Base[]. When you look at the built-in type definition of the .filter method in TypeScript:

Type definition of .filter

Oh, there seems to be another way? It appears that you can pass a type parameter to the .filter method, or explicitly define a type predicate in the signature part of the function passed to .filter. TypeScript's "type predicates" can be used to customize type assertions, so we can do it like this:

const arrOfExt1 = arr
  .filter((item): item is Extend1 => item.type === 't1')

//           ?^ Extend1[]

Following the principle of separation of concerns, the side using .filter should belong to business logic, and the predicate function passed in is a standalone util. This type assertion function may be reused multiple times, so consider extracting the predicate function:

function isExtend1(item: Base): item is Extend1 {
  return item.type === 't1'

const arrOfExt1 = arr.filter(isExtend1)

In the end, the result we see is very concise, intuitive, and readable.

Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.