写点什么

带你体验一次类型编程实践

作者:小鑫同学
  • 2022 年 8 月 20 日
    北京
  • 本文字数:1861 字

    阅读完需:约 6 分钟


大家好,我是小鑫同学。一位从事过 Android 开发混合开发,现在长期从事前端开发的编程爱好者,我觉得在编程之路上最重要的是知识的分享,所谓三人行必有我师。所以我开始在社区持续输出我所了解到、学习到、工作中遇到的各种编程知识,欢迎有想法、有同感的伙伴加我 fe-xiaoxin 微信交流~

写作背景:

在看 uniapp 的教程时看到大量的 API 还是使用的 callback 的方式来接收 API 的执行结果,大量的 API 嵌套使用又会造成回调地狱的现象出现,在 API Promise 化 这一篇中提到了有部分 API 是已经做了 Promise 化,我这边用 cli 命令初始化的 vite+ts 的项目发现没办法使用对应的 Promise 化 API,所以还是通过一个工具类来实现一下,顺便试着再写一写 TypeScript 类型编程代码。

工具类编写准备:

下面这块代码我相信你有过类似想法的 jym 应该在网上看到过,通过定义这样一个高阶函数来将 uniapp api 进行包装,并在执行这个高阶函数返回的函数时使用 Promise 来接管 api 成功失败所对应的回调函数。


export const promisify = (api) => {  return (options, ...params) => {    return new Promise((resolve, reject) => {      api(Object.assign({}, options, {        success: resolve,        fail: reject      }), ...params);    });  }}
复制代码


注:高阶函数说的简单点就是传入一个函数并返回一个函数,切记返回的是函数还没有执行,遇到了多少写防抖节流的小伙伴是忘了执行还各个群里问 why 的~

发挥 TypeScript 类型的强大之处:

Typescript 内置类型工具:

  1. Parameters<T>:提取函数类型的参数所组成的类型列表;

  2. NonNullable<T>:提取传入类型除 null、undefined 以外的类型;

类型编程分析:

  1. promisify 函数的输入类型约束:输入的内容均是 uniapp api(函数),所以使用泛型来约束输入的类型;


const promisify = <P extends (...args: any) => any>(api: P) => {}
复制代码


  1. promisify 返回的函数的输入类型约束:这个输入类型实际是 uniapp api 执行的的形参类型,所以需要使用内置的类型工具(1)来提取,我们只提取类型列表的第一项即可,有实际需要可以再扩展:


type ParameterFirst<T extends (...args: any) => any> = Parameters<T>[0];
export const promisify = <P extends (...args: any) => any>(api: P) => { return (options: ParameterFirst<P>) => {}}
复制代码


  1. 执行完 promisify 返回的函数后 Promise 对象的类型约束:这里只能通过泛型约束成功状态的类型,成功状态的类型实际上是 uniapp api 选项中 success 属性(回调函数)返回的类型。我们需要先提取到 success 属性,然后再次使用内置类型工具(1)来提取回调函数的返回类型。


type ParameterSuccess<T extends (...args: any) => any> = ParameterFirst<NonNullable<Parameters<T>[0]['success']>>;
复制代码


注:因为 Parameters<T>存在可能得到一个 undefined 类型的情况,所以使用 NonNullable<T> 来进行包装。

完整的 promisify 工具类:

/** *  uni.request({ *      url: 'https://jsonplaceholder.typicode.com/todos/1', *      success: (res) => { *          console.log(res.data); *      } *  }); *   *   promisify(uni.request)({ url: '' }).then(res => { *     // res.data *   }).catch(err => { * *   }); *   * @param api  * @returns  */
/** * 提取传入函数的第一个形参参数的类型 */type ParameterFirst<T extends (...args: any) => any> = Parameters<T>[0];/** * 提取传入函数的第一个形参参数中key为success的参数的类型; * 因success类型为函数类型,所以再次提取success函数的第一个形参参数的类型 */type ParameterSuccess<T extends (...args: any) => any> = ParameterFirst<NonNullable<Parameters<T>[0]['success']>>;
export const promisify = <P extends (...args: any) => any>(api: P) => { return (options: ParameterFirst<P>) => { return new Promise<ParameterSuccess<P>>((resolve, reject) => { api( Object.assign({}, options, { success: resolve, fail: reject, }), ); }); };};
复制代码

结语:

既然选择 TypeScript 来编写项目,就要尽可能的发挥出 TypeScript 作用,在万般无奈的时候再用 any 也不迟 ~~~


如果看完觉得有收获,欢迎点赞、评论、分享支持一下。你的支持和肯定,是我坚持写作的动力~

最后可以关注我 @小鑫同学,共同进步(还可以帮你 fix🐛)~

发布于: 刚刚阅读数: 3
用户头像

小鑫同学

关注

公众号:前端小鑫同学 2018.12.10 加入

我是小鑫同学6年前端及跨平台开发经验,曾独立设计混开框架和重构方案,掘金/51CTO活跃作者~

评论

发布
暂无评论
带你体验一次类型编程实践_8月月更_小鑫同学_InfoQ写作社区