往期精彩

  • 基于NodeJS从零构建线上自动化打包工作流(H5-Dooring特别版)
  • 在线IDE开发入门之从零实现一个在线代码编辑器
  • 基于React+Koa实现一个h5页面可视化编辑器-Dooring
  • TS核心知识点总结及项目实战案例分析

前言

以前一直不会用infer,要么直接就是returnType,压根不需要用infer,网上那些教程只给示例不给具体场景就无法让人很好理解这玩意。

类型分发

对于infer,最好应该先说一下类型分发,虽然他们关系不是太大,但是如果把infer与类型分发结合起来,让人一看就觉得这人ts水平可以。至于协变逆变等概念会比较容易让人搞混乱,可以以后再掌握。

我以前也学过这个,但是并不是能完全掌握它的使用时机,也不知道如何用,所以看别人用能看懂和自己能用完全是2种状态。

首先看一下类型分发的基本例子:

interface Fish {    fish: string}interface Water {    water: string}interface Bird {    bird: string}interface Sky {    sky: string}//naked typetype Condition = T extends Fish ? Water : Sky;let condition1: Condition = { water: '水' };let condition2: Condition = { sky: '天空' };

相信这个例子大家很容易理解,但是实际中什么时候用,怎么用,完全不知道。

这个例子有个特点,就是下面的condition1condition2里定义的类型里所传的泛型与后面赋值的类型并不一样。

也就是说,类型分发一般是用来先知道已知类型,赋的值的类型会基于这个分发进行判断推出相应类型。

乍看之下好像还是没什么卵用,比如condition1,我都知道类型,我直接写个Sky|Water类型不香?干嘛二货的还搞个类型分发?

上面那个例子确实没啥卵用,但是如果判断继承的也是泛型,那么就可以快速取出一些类型,而不用自己重新去定义:(虽然这些很多都是内置的)

type Diff = T extends U ? never : T;type R = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "b" | "d"type Filter = T extends U ? T : never;type R1 = Filter<string | number | boolean, number>;

既然有内置的,还不是没卵用。。。所以这就需要和infer联合使用才能看出牛b之处。

infer初探

infer大家应该都知道,returnType就是infer搞得,代码是这样:

type ReturnTypeextends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;

乍看之下好像有点难懂,其实仔细看发现还是很好理解的,它也是个类型分发。学到这里,很多人可能就只知道有这个东西,但是什么时候用Infer完全不知道,我也是这样,后来再次听课时突发灵感,发现这个infer其实就相当于占位,也就是一个不知道的类型,用infer X去给他占位,再结合类型分发,就能玩出花样来了。当时还有小伙伴这么问:ts不是能自动推断类型吗?为什么需要Infer X去推断类型。卧槽,这个问的太好了,这个就是理解Infer的关键。我们先结合个示例来进行说明:

export {}type Parameters = T extends (...args: infer R) => any ? R : any;type T0 = Parameters<() => string>;  // []type T1 = Parameters<(s: string) => void>;  // [string]type T2 = Parameters<((arg: T) => T)>;  // [unknown]

这个parameter也是内置的,可以看见,也是个类型分发,跟returnType区别就是infer X的占位跑到参数上去定义类型了。

如果我们把infer R换成已知类型,那么这个类型分发就跟一开始的demo没太大区别:

type Parameters = T extends (...args:string[]) => any ? string[] : any;type T0 = Parameters<() => string>; 

如果不换成已知类型,那么只写R不写infer会报错,因为不知道R是什么东西。

那么如果通过泛型传呢?可惜args必须是个数组类型,所以用泛型传还得限定下它的条件:

type Parametersextends type T0 = Parameters<() => string,string[]>; 

可以发现,这么传跟已知类型传其实没太大区别,因为在传第二个泛型的时候,这个类型我们是知道的,所以这种情况,也没什么太大用处,除非传泛型的是另一个人,那么我们在写这个库的时候,倒是可以拿到用户所定义的类型。这时倒是有点作用。

这样一换就可以发现,infer可以在类型推导中去占任何位置,最后的推导的类型可以借助这之间所需的类型。可以看下这个例子加深理解:

  type T1 = { name: string };  type T2 = { age: number };    type UnionToIntersection = T extends { a: (x: infer U) => void; b: (x: infer U) => void } ? U : never;  type T3 = UnionToIntersection(x: T1) => 

这个例子就是infer取得参数,两个函数的参数,对于为啥2个会出来交叉类型,这里是协变,所以是交叉类型。

下面看一下更难点的例子,来源于leetcode招聘:

https://github.com/LeetCode-OpenSource/hire/blob/master/typescript_zh.md

题目是这样:

interface Action {    payload?: T;    type: string;  }    class EffectModule {    count = 1;    message = "hello!";      delay(input: Promise<number>) {      return input.then(i => ({        payload: `hello ${i}!`,        type: 'delay'      }));    }      setMessage(action: Action<Date>) {      return {        payload: action.payload!.getMilliseconds(),        type: "set-message"      };    }  }    // 修改 Connect 的类型,让 connected 的类型变成预期的类型  type Connect = (module: EffectModule) => any;    const connect: Connect = m => ({    delay: (input: number) => ({      type: 'delay',      payload: `hello 2`    }),    setMessage: (input: Date) => ({      type: "set-message",      payload: input.getMilliseconds()    })  });    type Connected = {    delay(input: number): Action<string>;    setMessage(action: Date): Action<number>;  };    export const connected: Connected = connect(new EffectModule());

要求修改那个any,使其返回正确类型,而且这个类型要和connected一样。

有同学说,直接把any改成connected不就完了?要是这么简单也不会出这题。这个肯定是要你推出来,并且这个connected它的类型是EffectModule实例上的方法,里面的参数与返回还修改了。

这题怎么做呢,先一步步来,先提取出effectModule的方法,不然没法下一步。

提取class方法没有现成的,肯定不能keyof EffectModule,因为还有别的东西,怎么排除别的玩意呢?就是利用类型分发和class可以取值来做,如果是函数,那就提取,否则就不提取:

  type MethodName = {[F in keyof T]:T[F] extends Function ? F:never}[keyof T]  type EE =  MethodName

这里同时利用value如果是never 则keyof就不会返回。这段其实挺有启发性,因为很多时候,都想搞个循环判断类型,然后进行选择,这就是个很好的范例。

拿到了name然后要改装方法它需要:

asyncMethod(input: Promise): Promise> asyncMethod(input: T): ActionsyncMethod(action: Action): Action  syncMethod(action: T): Action

这个是题目给的,直接抄来:

type asyncMethod = (input: Promise) => Promise> type asyncMethodConnect = (input: T) => Actiontype syncMethod = (action: Action) => Action type syncMethodConnect = (action: T) => Action

然后需要做一个类型分发,用来判断是哪个方法,再分发给哪个方法:

type EffectMethodAssign = T extends asyncMethod     ? asyncMethodConnect     : T extends syncMethod     ? syncMethodConnect     : never

这段很简单,就是分发判断,泛型是用infer占位ok。最后,修改connect,就大功告成.

  type Connect = (module: EffectModule) => {      [F in MethodName<typeof module>]:EffectMethodAssign<typeof module[F]>  }

infer的用法_typescript高级用法之infer的理解与使用相关推荐

  1. 【Linux防火墙】iptables基础用法及高级用法

    文章目录 iptables命令简介 1.语法 2.iptables命令选项输入顺序 3.选项讲解 4.基础用法示例 1.清除规则相关操作 2.查看相关规则及序列号 3.通过查看后的序列号删除规则 4. ...

  2. shell+中sum的用法_shell高级用法

    1.if语句 1.1.回顾 在运行脚本前,我们一般先对脚本的语法进行检查,如果脚本有bug,我们再调试脚本: (1)语法检查:bash -n 脚本路径 (2)脚本调试:bash -x 脚本路径 1.2 ...

  3. shell if语句特殊用法(高级用法),工具安装判断

    for i in wget gcc doif [ ! `rpm -qa | grep ^$i-[0-9].` ] ; thenyum install -y $i[ $? -ne 0 ] &&a ...

  4. linux sed高级用法,sed 高级用法

    首先,应该明白模式空间的定义.模式空间就是读入行所在的缓存,sed对文本行进行的处理都是在这个缓存中进行的.这对接下来的学习是有帮助的. 在正常情况下,sed将待处理的行读入模式空间,脚本中的命令就一 ...

  5. python if高级用法_Python高级用法总结--元类

    type() 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义,而是运行时动态创建的. 比方说我们要定义一个 Hello 的 class ,就写一个hello.py 模块: class ...

  6. python自动填日志_Selenium3+python自动化012+日志logging基本用法、高级用法

    1.关键字: login 登录 log 日志 logging python日志模块 2.什么叫日志: 日志用来记录用户行为或者代码的执行过程 3.日志使用的地方: 1.排错的时候需要打印很多细节来帮助 ...

  7. java静态方法高级用法_Spock高级用法 – 动态mock

    这是Spock系列的第八篇文章,上一篇介绍了Spock如何使用power mock测试静态方法,这篇讲解Spock自带的mock功能如何和power mock组合使用,发挥更强大的作用 动态mock静 ...

  8. Angular - ng-repeat高级用法

    Angular - ng-repeat高级用法 ng-repeat高级用法: 遍历数组:     <li ng-repeat="item in array">{{ite ...

  9. infer的用法_infer是什么意思|infer的音标|infer的用法 - 英语词典

    infer的意思.解释 过去式:inferred;   过去分词:inferred; 现在分词:inferring; infer 基本解释 及物动词推断; 猜想,推理; 暗示; 意指 不及物动词作出推 ...

最新文章

  1. 跟我学习php文件和目录常用函数-下篇
  2. Dotween的timeScale
  3. 用栈实现队列与用队列实现栈
  4. Python Django 自定义Manager实现批量删除(逻辑删除)
  5. python中的函数修饰符
  6. mysql explain语句_Mysql explain 语句详解
  7. perl mysql dml_MySQL Connector执行SQL语句的三种方式
  8. 2018年手机摄像头模组市场调研报告
  9. 1.RABBITMQ 入门 - WINDOWS - 获取,安装,配置
  10. openmv探索_2_追踪单颜色
  11. 基于R语言的方差分析
  12. 小管家进销存_管家婆物联宝微订货V2.3发版公告
  13. android图片颜色识别器,颜色识别器APP
  14. HSC-1th misc——DORAEMON
  15. c语言中f的作用是什么,c语言中f什么意思 c语言中f什么意思
  16. 萌新带你开车上p站(一)
  17. vue实现时间段选择组件,分星期,最小粒度半点
  18. Laya Air游戏开发模式之MVC模式(核心篇)
  19. matlab处理时间数据绘图
  20. 网络初识 —— 子网划分

热门文章

  1. html表格标签模板 实现跨行和跨列
  2. 准备写java学习笔记
  3. Domino/Xpages Bootstrap 动态生成首页功能
  4. React + Typescript + Webpack 开发环境配置
  5. 百度地图的简单使用 ——html js
  6. Android小项目之---选择对话框(附源码)
  7. 不重启iis的情况下切换iis的.net版本
  8. 难道这又是个未解之谜?--- 关于DLL中使用ADODATASET出错的问题
  9. Linux fs清理文件,linux找出已经删除但磁盘空间未释放的大文件并清空
  10. python区间分组_分组匹配