通过 Lodash 学 TS —— 实现 pick 方法

简介: 一、前言最近学习TS,被复杂的类型体操搞的比较晕, 经常想学了那么高深的体操后是不是有用武之地,技术的学习应该是要以落地为目的,要不然很快就忘记了, 其实我在业务开发中用ts用的用的比较浅, 想框架源码中那么高深的ts,我暂时还没有用到过, 还没到给框架提merge的水平, 其实用ts最多的还是方法类库,毕竟经常打交道,而且同事很大可能会用你写的方法, 所以学着给方法加TS,是个不错的选择 处。

一、前言

最近学习TS,被复杂的类型体操搞的比较晕, 经常想学了那么高深的体操后是不是有用武之地,技术的学习应该是要以落地为目的,要不然很快就忘记了, 其实我在业务开发中用ts用的用的比较浅, 想框架源码中那么高深的ts,我暂时还没有用到过, 还没到给框架提merge的水平, 其实用ts最多的还是方法类库,毕竟经常打交道,而且同事很大可能会用你写的方法, 所以学着给方法加TS,是个不错的选择

lodash 是这个不错的学习对象, 他的源码其实是js 写的, ts的支持是 @types/lodash 这个npm包中给声明的, 所以我们可以学着这个库给lodash 的方法添加 ts

这是这个库的代码链接

这个库很大, 找起来很不方便, 强烈推荐 一个浏览器插件 Sourcegraph

屏幕截图 2023-07-05 231252.png

安装上这个之后,在刷新,github页面会有一个按钮,点击会跳转打开一个带有树状层级,可以自由检索内容的页面屏幕截图 2023-07-05 231315.png

可以 command + P 搜索 pick 就很容易能找到想找的内容

二、实现

1、pick 的基本用法

先看看pick的基本用法, 打开 lodash 官网, 链接

var object = { 'a': 1, 'b': '2', 'c': 3 };  
_.pick(object, ['a', 'c']);  
// => { 'a': 1, 'c': 3 }

我们就接下来就用简单的代码实现上面的例子,并给方法添加ts, 忽略lodash _.pick 中的细节,以及方法的兼容性 写法 等问题

function pick(target={}, keys) {
  let result = {};
  Object.keys(target).map((el) => {
    if (keys.includes(el)) {
      result[el] = target[el];
    }
  });
  return result;
}
var result = pick({ a: "1", b: "2", c: 3 }, ["a", "c"]);  
// => { 'a': 1, 'c': 3 }

代码比较简单, 传入两个参数, 一个是对象, 一个是对象的key的数组, 返回一个对象

2、给 pick函数 添加 ts

添加ts 之前 我们需要先明白 为什么要给这个函数添加 ts

其实是跟这个函数的作用 有关的, 这个函数 用就是通过传入对象和对象 key的数组,去匹配出,想要的值并组成 对象返回

所以ts要做的就是以下几点

  • 1、基础版 —— 基础的是,约束函数的入参和返回值(入参是对象和数组,返回值是对象)
  • 2、进阶版 —— 因为两个入参是有关联关系的, 最好能体现出关联关系(keys 中的值要约束成 target对象中的键 )
  • 3、最终版 ——对返回值能约束的更准确, 这句话的意思就是说, 在返回的对象中, 能明确知道取值后的类型,比如返回的是 result = {a:'1'} , 那么 result.a 应该能推断出来是 string 类型的, 同理 result.c 是数字类型的, 目的就是方便接下了的操作,避免因为考虑不到,调用了不属于该类型的方法导致出现错误, 一言以蔽之,就是约束的类型约准确, 编辑器的提示就能更准确, 就更能避免错误到运行时才发现

1. 实现基础版

下面我们先给pick添加最简单最弱的约束

function pick(target: object, keys: any[]): object
function pick(target={}, keys) {
  let result = {};
  Object.keys(target).map((el) => {
    if (keys.includes(el)) {
      result[el] = target[el];
    }
  });
  return result;
}

我们可以在不改动函数的前提下,在写一个函数的规定入参与返回值,也可以写多个, 叫函数的重载

写完这个之后, 再使用的时候, 编辑器就开始提示, 这里需要两个入参,一个对象,一个数组,返回的是一个数组

屏幕截图 2023-07-05 231414.png

写到这里就完成了最基本的一步, 约束函数的入参和返回值, 能保证基本的用法不出错了, 接下了就是更进一步的完善,让提示的内容更多,约束更强,后续的使用更清晰

我们完成第一步的基本后, 两个参数还是没有任何关联的状态, 其实第二个入参,应该约束为第一个入参的key, 然后我们开始给函数使用泛型

2. 实现进阶版

function pick<T extends object>(target: T, keys: (keyof T)[]): T

解释: extends 有两个种用途, 一是约束,上文中所写,约束定义的 T 是一个 object类型, 而是添加判断 extends ? true: false keyof T : 意思是取T的索引key , keys 意思就是约束为object对象的索引组成的数组

完成这步之后, 再使用的时候,编辑器就会提示我们第二个入参要是第一个入参的key, 这个目标就完成了, 如果输入了对象中没有的key, 那么就要报错提示了

屏幕截图 2023-07-05 231450.png

完成了第二步,然后我们继续处理返回结果, 目前的返回结果是T, 意思就是传入的object的类型, 并不是返回对象的类型, 我们需要继续改进,让返回结果也能正确提示处理

屏幕截图 2023-07-05 231511.png

上图中, 其实返回结果取值后的类型已经是正确的了, 但是存在一些小问题, 当result取c的时候, 不会报错, 接下来我们需要让取c报错, 只能取约束的key

3. 实现最终版

这时我们需要再加一个泛型参数来表示第二个入参的类型

function pick<T extends object, U extends keyof T>(target: T, keys: U[]): {
   [P in U]: T[U]
}

解释: 在之前基础上,新增一个泛型参数 U 约束为 T 的索引类型组成的联合类型, keys 自然就是 这个联合类型组成的数组, 在返回值上, 返回的是一个对象, [P in U] 意思就是 取联合类型的 值作为新的索引 值 T[U] 可以理解为 target对象内 U 的 value

此时就完成了 给这个简单函数增加ts的能力, 然后再看一下, @types/lodash 中怎么写的

function pick<T extends object, U extends keyof T>(
  object: T,
  keys: readonly U[]
): Pick<T, U>;

其实看起来我们的已经很接近了, 只是返回值这里, 库中直接用的ts中的封装好的Pick方法,意思就是从T中匹配U来

type Pick<T, K extends keyof T> = { [P in K]: T[P]; }

屏幕截图 2023-07-05 231603.png

看一下ts中Pick的实现, 大致和我们pick函数类似, 但是他是处理类型的,从一个对象类型中,拿取指定的类型

三、总结

现在ts很流行,很多框架库中都支持了TS,但是对于我们普通的开发者,很少接触那么高深的 TS 类型编程, 业务中也总是由于忙于开发, 常常写成了 anyscript 工程师, 看到类型提示错误后, 心中一万匹xx掠过, 常常不知道为什么写ts , 感觉只会限制我们拔刀的速度, 于是就很容易陷入,太难的看不懂, 简单的不屑写的境界, 但是类库方法的ts支持对我们日常开发,是非常重要的, 毕竟业务代码大概率别人用不到,但是方法代码,还是很容易就用到的, 还是需要用心去写, 那么作为类库中大家熟知的lodash就是很好学习的目标, 我们给pick方法添加ts, 从基础版,简单约束传参与返回值后,一步步进阶学习了泛型,以及基于泛型的类型编程, 然后对比  @types/lodash  的实现,顺便学了一下 Pick 这个Ts 内置的高级类型

相关文章
|
JavaScript
ts常用的语法
ts常用的语法
|
3月前
|
JavaScript
TS语法忽略、eslint忽略
TS语法忽略、eslint忽略
103 1
|
5月前
|
JavaScript
TS,编写TS文件,编写hello.ts文件,cmd下打开终端,输入tsc hello.ts会生成一个hello.js文件,简化命令,npm i -g ts-node,ts-node hello.ts
TS,编写TS文件,编写hello.ts文件,cmd下打开终端,输入tsc hello.ts会生成一个hello.js文件,简化命令,npm i -g ts-node,ts-node hello.ts
|
JavaScript 前端开发 开发者
ts详解以及相关例子(一篇带你详细了解ts)
ts详解以及相关例子(一篇带你详细了解ts)
171 1
|
7月前
|
JavaScript 前端开发
设置Angular路径别名用于ts、js、scss、less
设置Angular路径别名用于ts、js、scss、less
|
7月前
|
JavaScript 前端开发 编译器
ts和js的区别?
ts和js的区别?
99 0
|
7月前
|
JavaScript
【Vue Error】 error Component name “product“ should always be multi-word vue/multi-word-compone……
【Vue Error】 error Component name “product“ should always be multi-word vue/multi-word-compone……
|
前端开发
react在ts中提示ref问题
react在ts中提示ref问题
107 0
|
JavaScript 前端开发 IDE
什么是ts,作用是什么
什么是ts,作用是什么
1359 0
|
JavaScript 前端开发 编译器
TS_React:Hook类型化
依赖类型推断 类型化 useState 类型化 useReducer 类型化 useRef 类型化 forwardRef 类型化 useEffect 和 useLayoutEffect 类型化 useMemo 和 useCallback 类型化 useContext 类型化自定义hook
TS_React:Hook类型化