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 后的断言" 这件事来说还有更好的解法。

问题的核心在于 .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 函数是很独立的 util,这个类型断言函数可能复用多次,因此考虑将判断函数抽出来:

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

const arrOfExt1 = arr.filter(isExtend1)

最后我们看到的结果也非常简洁、直观和易读。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。