实现一个类型完善的 lodash.get

通过类型计算,为 lodash.get实现完善的出入参类型推断

代码在此,可以体验

前言

众所周知,滥用?.符号会使项目的体积快速增长。点我看看体积是怎么爆炸的

使用 lodash.get工具能安全的取对应对象的值。但是使用_.get无法拥有完整的类型分析,也就是说,咱们写的定义文件如果通过 get 方法取值,则无法享受到 type 类型福利。

能不能有一个既类型完善,又安全的 get方法呢?让咱们来造一个轮子。

效果

解读

  1. 针对传入的对象和 pathChain,能获取到对应的值。
  2. 基于传入的对象,能生成 pathChain,输入正确的 pathChain,能识别出 pathChain 指向对象的类型。
  3. 再也不用写?.来增加项目体积了。

代码实现

我们举例最简单的一种类型,即将对象取值的 pathChain 以.连接,则只需要将传入的 pathChain使用 split 方法拆成数组,并通过循环遍历的方式取值。此处简单实现取值逻辑,着重演示下面类型实现。

类型实现

众所周知,可以用 typescript 的类型来计算,下面来用类型系统根据函数第一个入参 计算出第二个入参 ,和出参的类型

Keyof

Keyof 用于接收对象后,获取对象/数组上面的值。

ts 复制代码
// ts里面 extends 可以作为一个判断节点使用
// 例如 type a = 1 extends number ? true : false
// 此时 a === true
type Keyof<T> = T extends any[]
  ? number
  : T extends Record<any, any>
  ? keyof T
  : never;

Split

Split用于接收访问链路后,分解成数组,并进行后续计算。

ts 复制代码
// infer 用于在递归中获取对应位置的值,例如下方的 infer F用于获取第一个点前面的值
// 如果获取到了,则在第一个问号处可以使用infer 推断出的值,相当于声明了一个变量
// 注意:通过 infer 获取到的值在冒号后面无法正常使用。
type Split<T extends string> = T extends `${infer F}.${infer Rest}`
    // 此处有一个递归
  ? [F, ...Split<Rest>]
  : [T];
  
Split<'1.2.3'> = ['1', '2', '3']

GetChain

GetChain 用于接收第一个参数对象后,生成对象上所有的访问链。

ts 复制代码
 type GetChain<
 // 此处声明 T 是xxx 类型的子类型,K 声明在 T 后面,因此可以使用 T。
 // 注意此处 K 使用了 = 符号,因此是一个默认值。
  T extends Record<any, any> | Array<any> | undefined,
  K extends Keyof<T> = Keyof<Required<T>>
> = T extends undefined
// never 在类型计算中有什么意义待我温习一下补充
  ? never
  : K extends string | number
  // 返回当前找到的 key,并往其value 进行递归
  ? `${K}` | `${K}.${GetChain<Required<T[K]>>}`
  : "";
  {}
  {a?: {b?: number}}
  'a'

PickValue

PickValue 用于传入对象、取值链后,取指定对象的类型。

ts 复制代码
type PickValue<
  O extends Record<any, any>,
  Chain extends string[],
  // Nul用于判断当前元素是否可能为 undefined
  Nul extends boolean = false
  // 注意这里的  Rest extends string[],在 infer 中使用 extends 进线类型判断
  // 需要 ts 4.7 支持
> = Chain extends [infer Head, ...infer Rest extends string[]]
// 类型守卫,ts 有些时候无法正确推断出 Head 的类型
// 可能会有问题这里 Head 的类型为啥不放在 infer  使用 extends 语法进行判断,
// 这里的原因我忘了
  ? Head extends string
  // 还是一个递归
    ? PickValue<
        Required<O>[Head],
        Rest,
        // 将当前元素是否可能为 undefined 进线透传+判断
        // 如果前一个元素可能为 undefined,则当前元素也可能为 undefined
        Nul extends true ? true : undefined extends O[Head] ? true : false
      >
    : O
  : Nul extends true
  ? O | undefined
  : O;
  
  PickVlaue<{a: number}, 'a'> = number

函数声明

函数入参、出参类型

ts 复制代码
// T是第一个入参的类型,P 是第二个入参的类型。
// 如果不在调用函数的时候显式传入类型,ts 会从参数中自动推断。
function _get<T extends any[] | Record<any, any> | null | undefined, P extends GetChain<T>>(
  o: T,
  pathChain: P
): PickValue<T, Split<P>> {
  const path = pathChain.split(".");
  let result = o;
  try {
    for (let i = 0; i < path.length; ++i) {
      result = result[path[i]];
    }
  } catch (e) {}
  return result as any;
}
相关推荐
牧羊狼的狼1 小时前
React 中的 HOC 和 Hooks
前端·javascript·react.js·hooks·高阶组件·hoc
知识分享小能手3 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
luckys.one3 小时前
第9篇:Freqtrade量化交易之config.json 基础入门与初始化
javascript·数据库·python·mysql·算法·json·区块链
魔云连洲3 小时前
深入解析:Vue与React的异步批处理更新机制
前端·vue.js·react.js
mCell3 小时前
JavaScript 的多线程能力:Worker
前端·javascript·浏览器
weixin_437830945 小时前
使用冰狐智能辅助实现图形列表自动点击:OCR与HID技术详解
开发语言·javascript·ocr
超级无敌攻城狮5 小时前
3 分钟学会!波浪文字动画超详细教程,从 0 到 1 实现「思考中 / 加载中」高级效果
前端
excel5 小时前
用 TensorFlow.js Node 实现猫图像识别(教学版逐步分解)
前端
gnip6 小时前
JavaScript事件流
前端·javascript
小菜全6 小时前
基于若依框架Vue+TS导出PDF文件的方法
javascript·vue.js·前端框架·json