白话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>; // => numberlet demo2: NonNullable<string>; // => stringlet 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的分支中使用。
// 内置 ReturnTypetype 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>; // => booleanlet func2: Func<''>; // => booleanlet 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>; // => numberlet obj2: Obj<true>; // => numberlet obj3: Obj<{a: number, b: number}>; // => numberlet obj4: Obj<{a: number, b: () => void}>; // => number | () => void例子三(Vue3中的UnwrapRef)
// 假如泛型变量T是ComputedRef的'子集',那么使用UnwrapRefSimple解决infer指代的ComputedRef泛型参数V// 否则进一步判断能否为Ref的'子集',进一步UnwrapRefSimpleexport 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,调用UnwrappedObjecttype 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>// 我是分割线 // 泛型Refexport 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推断的效率和代码类型的可读性。
如有问题,欢迎指出。
劳动节快乐!
image
说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 白话typescript中的【extends】和【infer】(含vue3的UnwrapRef)
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 白话typescript中的【extends】和【infer】(含vue3的UnwrapRef)