白话typescript中的【extends】和【infer】(含vue3的UnwrapRef)
大家好,我是小雨小雨,致力于分享有趣的、实用的技术文章。
内容分为翻译和原创,如果有问题,欢迎随时评论或私信,希望和大家一起进步。
分享不易,希望能够得到大家的支持和关注。
extends
typescript 2.8引入了条件类型关键字: extends,长这个样子:
T extends U ? X : Y
看起来是不是有点像三元运算符: condition ? result(1) : result(2),用大白话可以表示为:
如果
T包含的类型 是U包含的类型的 '子集',那么取结果X,否则取结果Y。
再举几个ts预定义条件类型的例子,加深理解:
type NonNullable<T> = T extends null | undefined ? never : T;
// 如果泛型参数 T 为 null 或 undefined,那么取 never,否则直接返回T。
let demo1: NonNullable<number>; // => number
let demo2: NonNullable<string>; // => string
let demo3: NonNullable<undefined | null>; // => never
分配式extends
T extends U ? X : Y
其实就是当上面的T为联合类型的时候,会进行拆分,有点类似数学中的分解因式:
(a + b) * c => ac + bc
再举个官网的例子:
type Diff<T, U> = T extends U ? never : T; // 找出T的差集
type Filter<T, U> = T extends U ? T : never; // 找出交集
type T30 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">; // => "b" | "d"
// <"a" | "b" | "c" | "d", "a" | "c" | "f">
// 相当于
// <'a', "a" | "c" | "f"> |
// <'b', "a" | "c" | "f"> |
// <'c', "a" | "c" | "f"> |
// <'d', "a" | "c" | "f">
type T31 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">; // => "a" | "c"
// <"a" | "b" | "c" | "d", "a" | "c" | "f"> 同上
let demo1: Diff<number, string>; // => number
我们再来看看infer。
infer
在extends语句中,还支持infer关键字,可以推断一个类型变量,高效的对类型进行模式匹配。但是,这个类型变量只能在true的分支中使用。
// 内置 ReturnType
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
不知道初学ts的朋友们看完这个介绍是不是一脸懵逼,反正之前我是...
其实理解之后很简单,这里直接说下我的理解,应该还算简单易懂:
infer X 就相当于声明了一个变量,这个变量随后可以使用,是不是有点像for循环里面的声明语句?
for (let i = 0, len = arr.length; i < len; i++) {
// do something
}
不同的是,infer X的这个位置本应该有一个写死的类型变量,只不过用infer R替换了,更灵活。
需要注意的是infer声明的这个变量只能在true分支中使用
还是举几个例子,加深理解,纸上谈兵终觉浅嘛:
例子一
// 解读: 如果泛型变量T是 () => infer R的`子集`,那么返回 通过infer获取到的函数返回值,否则返回boolean类型
type Func<T> = T extends () => infer R ? R : boolean;
let func1: Func<number>; // => boolean
let func2: Func<''>; // => boolean
let func3: Func<() => Promise<number>>; // => Promise<number>
例子二
// 同上,但当a、b为不同类型的时候,返回不同类型的联合类型
type Obj<T> = T extends {a: infer VType, b: infer VType} ? VType : number;
let obj1: Obj<string>; // => number
let obj2: Obj<true>; // => number
let obj3: Obj<{a: number, b: number}>; // => number
let obj4: Obj<{a: number, b: () => void}>; // => number | () => void
例子三(Vue3中的UnwrapRef)
// 如果泛型变量T是ComputedRef的'子集',那么使用UnwrapRefSimple处理infer指代的ComputedRef泛型参数V
// 否则进一步判断是否为Ref的'子集',进一步UnwrapRefSimple
export type UnwrapRef<T> = T extends ComputedRef<infer V>
? UnwrapRefSimple<V>
: T extends Ref<infer V> ? UnwrapRefSimple<V> : UnwrapRefSimple<T>
// 我是分割线
// 如果T为Function | CollectionTypes | BaseTypes | Ref之一的'子集',直接返回。
// 否则判断是否为数组的'子集',不是的话视为object,调用UnwrappedObject
type UnwrapRefSimple<T> = T extends Function | CollectionTypes | BaseTypes | Ref
? T
: T extends Array<any> ? T : T extends object ? UnwrappedObject<T> : T
// 我是分割线
// 调用UnwrapRef,产生递归效果,解决了ts类型递归
type UnwrappedObject<T> = { [P in keyof T]: UnwrapRef<T[P]> } & SymbolExtract<T>
// 我是分割线
// 泛型Ref
export interface Ref<T = any> {
[Symbol()]: true
value: T
}
// 我是分割线
export interface ComputedRef<T = any> extends WritableComputedRef<T> {
readonly value: T
}
// 我是分割线
export interface WritableComputedRef<T> extends Ref<T> {
readonly effect: ReactiveEffect<T>
}
建议自己捋一遍。
总结
ts提供的extends和infer大大增加了类型判断的灵活性和复用性,虽然用与不用都可以,但能熟练地使用高级特性将大大提升ts推断的效率和代码类型的可读性。
如有问题,欢迎指出。
劳动节快乐!
白话typescript中的【extends】和【infer】(含vue3的UnwrapRef)的更多相关文章
- 【翻译】在TypeScript中,Extends和Implements一个抽象类有什么不同
我们知道在TypeScript中一个类既可以被implement也可以被extends,有一些C#或java基础的同学可能会对此感到困惑,因为在上述两个面向对象的语言里面只有接口可以被implemen ...
- 如何理解 TS 类型编程中的 extends 和 infer
extends extends 在TS类型编程中用法(T extends U),表示 T 中的某些在 U 里面,比较难描述,用法如下: T extends U ? X : Y 分为两种情况理解更直观一 ...
- typescript 中的 infer 关键字的理解
infer 这个关键字,整理记录一下,避免后面忘记了.有点难以理解呢. infer infer 是在 typescript 2.8中新增的关键字. infer 可以在 extends 条件类型的字句中 ...
- JavaScript 和 TypeScript 中的 class
对于一个前端开发者来说,很少用到 class ,因为在 JavaScript 中更多的是 函数式 编程,抬手就是一个 function,几乎不见 class 或 new 的踪影.所以 设计模式 也是大 ...
- 十分钟教你理解TypeScript中的泛型
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者.原文出处:https://blog.bitsrc.io/understanding-generics-in-t ...
- 解读typescript中 super关键字的用法
解读typescript中 super关键字的用法 传统的js,使用prototype实现父.子类继承.如果父.子类有同名的方法,子类去调用父类的同名方法需要用 “父类.prototype.metho ...
- JavaScript 、TypeScript 中的 Boolean
boolean 是 JavaScript 中一种有趣的原始数据类型.在TypeScript中,非严格模式下("strictNullChecks": false),它总共允许4个值 ...
- TypeScript中的private、protected
首先我们要清楚 private . protected 现阶段只是javascript中的保留字(Reserved words),而非关键字(Keywords ).因此TypeScript中的纯类型声 ...
- 【TypeScript】如何在TypeScript中使用async/await,让你的代码更像C#。
[TypeScript]如何在TypeScript中使用async/await,让你的代码更像C#. async/await 提到这个东西,大家应该都很熟悉.最出名的可能就是C#中的,但也有其它语言也 ...
随机推荐
- C++语言实现顺序栈
C++语言实现顺序栈 在写C语言实现顺序栈的时候,我已经向大家介绍了栈的特点以及介绍了栈的相关操作,并利用C语言实现了相关算法.在这里小编就不在继续给大家介绍了,需要温习的可以去我的博客看看.在这篇博 ...
- AJ学IOS(06)UI之iOS热门游戏_超级猜图
AJ分享,必须精品 先看效果图 思路 需求分析 1,搭建界面 1>上半部分,固定的,用Storyboard直接连线(OK) 2>下半部分,根据题目的变化,不断变化和调整,用代码方式实现比较 ...
- .net批量更新(插入、修改、删除)数据库
思路: 1. 设置DataTable中每行的状态标识,即调用DataRow的方法setAdded().setModified().Delete() 2. 使用DataAdapter的Update(Da ...
- sqli-labs通关教程----21~30关
第二十一关 第二十一关我们正常登陆后看到,uname后面变成了一堆字母 这是经过base64编码之后的样子,所以就照葫芦画瓢,将我payload的uname后面的部分转码成base64,这里可以用正常 ...
- Jmeter发送jdbc请求进行大批量造数
创建批量造数脚本,一个简单的结构如下图所示, 1.线程组(10个线程重复运行2次,相当于造20个数) 2.用户定义变量(这是全局变量,用于后面随机筛选用) 3.数据库连接配置 4.计数器(用于主键递增 ...
- GNU的make命令、makefile编写
makefile简介 makefile可实现工程的自动化编译,只需一个make命令即可一键完成.makefile定义了一些规则,指定哪些文件需要先编译.后编译.重新编译等. 一般的C或者C++程序,都 ...
- 13.create-react-app 构建的项目使用代理 proxy
1. 正常运行 npm run eject 2. create-react-app 的版本在低于 2.0 的时候可以在 package.json 增加 proxy 配置, 配置成如下: "p ...
- CLDAPReflectionDDoS(CLDAP反射放大攻击)
CLDAP Reflection DDoS 0x01 LDAP: 全称为Lightweight Directory Access Protocol,即轻量目录访问协议,基于X.500标准: 目录服务就 ...
- eclipse添加方法注释
打开注释模板编辑窗口:Window ->Preferences->java -> Code Style -> Code Template->Comments type 设 ...
- python 基础篇 匿名函数
匿名函数基础 首先,什么是匿名函数呢?以下是匿名函数的格式: lambda argument1, argument2,... argumentN : expression 我们可以看到,匿名函数的关键 ...