白话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 extends null | undefined ? never : T; // 如果泛型参数 T 为 null 或 undefined,那么取 never,否则直接返回T。 let demo1: NonNullable; // => number let demo2: NonNullable; // => string let demo3: NonNullable; // => never 分配式extends T extends U ? X : Y 其实就是当上面的T为联合类型的时候,会进行拆分,有点类似数学中的分解因式: (a + b) * c => ac + bc 再举个官网的例子: type Diff = T extends U ? never : T; // 找出T的差集 type Filter = 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 我们再来看看infer。 infer 在extends语句中,还支持infer关键字,可以推断一个类型变量,高效的对类型进行模式匹配。但是,这个类型变量只能在true的分支中使用。 // 内置 ReturnType type ReturnType = 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 extends () => infer R ? R : boolean; let func1: Func; // => boolean let func2: Func<''>; // => boolean let func3: Func<() => Promise>; // => Promise 例子二 // 同上,但当a、b为不同类型的时候,返回不同类型的联合类型 type Obj = T extends {a: infer VType, b: infer VType} ? VType : number; let obj1: Obj; // => number let obj2: Obj; // => 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 extends ComputedRef ? UnwrapRefSimple : T extends Ref ? UnwrapRefSimple : UnwrapRefSimple // 我是分割线 // 如果T为Function | CollectionTypes | BaseTypes | Ref之一的'子集',直接返回。 // 否则判断是否为数组的'子集',不是的话视为object,调用UnwrappedObject type UnwrapRefSimple = T extends Function | CollectionTypes | BaseTypes | Ref ? T : T extends Array ? T : T extends object ? UnwrappedObject : T // 我是分割线 // 调用UnwrapRef,产生递归效果,解决了ts类型递归 type UnwrappedObject = { [P in keyof T]: UnwrapRef } & SymbolExtract // 我是分割线 // 泛型Ref export interface Ref { [Symbol()]: true value: T } // 我是分割线 export interface ComputedRef extends WritableComputedRef { readonly value: T } // 我是分割线 export interface WritableComputedRef extends Ref { readonly effect: ReactiveEffect } 建议自己捋一遍。 总结 ts提供的extends和infer大大增加了类型判断的灵活性和复用性,虽然用与不用都可以,但能熟练地使用高级特性将大大提升ts推断的效率和代码类型的可读性。 如有问题,欢迎指出。 劳动节快乐! image

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):