ts 类型体操之内置工具类型

NonNullable是一个比较简单的工具类型,它接受一个范型 T 作为参数,如果 T 是null或undefined,则返回never,否则返回 T 本身。NonNullable 早些年的实现如下所示:

ts

typeNonNullable = Textendsnull|undefined?never: T;

不记得 extends 关键字的可以回顾一下《ts 类型体操之内置工具类型(上)》的内容。extends 实际执行时是对联合类型T里的每一个元素分别进行条件判断。所以 NonNullable 通常也是用于联合类型操作,剔除联合类型中的 null 和 undefined:

ts

typeT0=NonNullable;// string | numbertypeT1=NonNullable;// string[]

不过在typescript 4.8后,NonNullable 被重写了,现在它的实现如下:

ts

typeNonNullable = T & {};

这个实现其实更简单,它利用了类型系统中的交叉(&)操作符,将 T 和一个空对象类型{}进行合并,从而剔除了 T 中的null和undefined。这里提几个八股小知识点:{}是除了 undefined 和 null 之外,所有类型的父类型。 所以{}和 undefined 或 null 的交叉类型是 never,而且其余的类型和{}交叉的结果是其本身。

以NonNullable<number | undefined>为例:

NonNullable<number | undefined>=>(number | undefined) & {} => number=>(number & {} ) | (undefined & {})=>number | never=>number

再补充一个八股 unknown 事实上等价于{} | undefined | null, 所以NonNullable<unknown>等于{},但是NonNullable<any>等于any。

Awaited<T>

Awaited 类型用于获取 Promise 的返回值类型。例如:

ts

typeT0=Awaited>;// stringtypeT1=Awaited>>;// numbertypeT2=Awaited>;// number | boolean

Awaited “方法”还是有点难度的:

该类型需要支持递归:它需要将嵌套的 Promise 的类型展开,直至得到 Promise 的最终返回值类型。

递归的结束条件是:对非 PromiseLike 的类型(没有 then 方法的对象类型)返回 never。

我们逐行解释上面的实现:

T extends null | undefined:如果 T 是 null 或者 undefined,则直接返回 T。这个判断是为了处理非严格模式下,null 和 undefined 的情况。在严格模式下,null 和 undefined 不能作为合法的 Promise。

T extends object & { then(onfulfilled: infer F, ...args: infer _): any }:这行很长,中心思想是:如果 T 是一个对象,并且该对象具有 then 方法,那么我们就可以认为它是一个 PromiseLike 类型。这里我们用到了infer关键字,它表示在类型推导过程中,将 then 方法的第一个参数类型提取出来,赋值给 F。若 T 不是 PromiseLike 类型,则直接返回 T。

F extends (value: infer V, ...args: infer _) => any:F由上一步推断得到,如果 then 方法的第一个参数是函数类型,那么我们就可以认为它是一个 Promise。我们再次用到了infer关键字,将 then 方法的第一个参数的类型提取出来,赋值给 V。若 F 不是函数类型,则不是一个合法的 Promise,直接返回 never。

Awaited<V>:递归地展开 V,直到 V 不再是 PromiseLike 类型为止。

原始版本虽然能看得懂,但是太麻烦了。我们自实现type challenge这道MyAwaited的时候可以用下面一个简化版代替:

ts

typeAwaited = TextendsPromiseLike ?Awaited : T;

PromiseLike<T>也是一个内置接口,表示一个具有 then 方法的对象类型——Promise 的鸭子类型。大家可以直接用。

PromiseLike<infer R>:表示将 PromiseLike 类型中的泛型参数 R 提取出来,然后递归调用 Awaited,直到递归到非 PromiseLike 的类型。这里有个知识点:infer 甚至可以推断出接口中的范型参数。比如Promise<string>,可以直接推断出 string

extends ? (...) : T: 我们之前提到过:extends 会遍历联合类型。对于boolean | Promise<number>这样的 case,extends 会分别对boolean和Promise<number>进行判断,最终返回 boolean | number。

chopard-shs.fdcpx.net

chopard-bjs.fdcpx.net

chopard-shenzhen.biaoshouhou.cn

chopard-gzs.biaoshouhou.cn

chopard-shs.audemarsweixiu.com

chopard-bjs.audemarsweixiu.com

chopard-shenzhen.hidcwatch.com

chopard-gzs.hidcwatch.com

chopard-shs.fjfsx.com

chopard-bjs.fjfsx.com

chopard-shenzhen.hntwx.cn

chopard-gzs.hntwx.cn

chopard-shs.hx626.com

chopard-bjs.hx626.com

chopard-shenzhen.watchjwf.cn

chopard-gzs.watchjwf.cn

chopard-shs.shjshdzb.com

chopard-bjs.shjshdzb.com

chopard-shenzhen.shmwatch.cn

chopard-gzs.shmwatch.cn

chopard-shs.gyjshd.com

chopard-bjs.gyjshd.com

chopard-shenzhen.zhcxb.cn

chopard-gzs.zhcxb.cn

chopard-shenzhen.jshdvip.com

chopard-gzs.jshdvip.com

chopard-shs.gyjshdzb.com

chopard-bjs.gyjshdzb.com

chopard-sys.jhpwd.cn

chopard-zzs.jhpwd.cn

chopard-shenzhen.wzjshd.com

chopard-gzs.wzjshd.com

chopard-shs.jsfltime.com

chopard-bjs.jsfltime.com

chopard-sys.watchwb.cn

chopard-css.watchwb.cn

chopard-shenzhen.watch-hdl.com

chopard-gzs.watch-hdl.com

chopard-shs.watchhdlb.cn

chopard-bjs.watchhdlb.cn

chopard-whs.watchhdli.cn

chopard-nbs.watchhdli.cn

chopard-shenzhen.watchrhf.cn

chopard-gzs.watchrhf.cn

chopard-shs.watchec.cn

chopard-bjs.watchec.cn

chopard-whs.watchda.cn

chopard-xms.watchda.cn

chopard-hzs.csjshd.com

chopard-njs.csjshd.com

NoInfer<Type>

NoInfer<Type>:用于防止 TypeScript 从泛型函数内部推断类型。它是一个固有类型,没有更底层的实现:

ts

// lib.es5.d.tstypeNoInfer = intrinsic;

它是 TypeScript 5.4 刚推出的一个内置类型,所以我们正好看看如何在某些情况下使用它来改进 TypeScript 的推理行为。如下例所示:通常的情况下编译器是可以从函数入惨里推断出 result 类型是'hello'

ts

constreturnWhatIPassedIn = (value: T) =>value;constresult =returnWhatIPassedIn('hello');//const result: 'hello'

但如果我们用NoInfer<T>来包装 value, NoInfer 使 value 无法成为有效推断来源 T。因此如下 result 被推断为 unknown。

ts

constreturnWhatIPassedIn = (value: NoInfer<T>) =>value;constresult =returnWhatIPassedIn('hello');//const result: unknown

我们需要明确提供范型才能获得 returnWhatIPassedIn 的返回类型:

ts

constresult = returnWhatIPassedIn<'hello'>('hello');// const result: "hello"

NoInfer 要解决什么问题呢?一个很好的例子是创建有限状态机 (FSM) 的函数。FSM 有一个 initial 状态和一个列表 states。initial 状态必须是 states 之一。

ts

declarefunctioncreateFSM(config: {initial:TState;states:TState[];}):TState;

请注意,TypeScript 可以从两个可能的地方推断类型:initial 和 states。如下所示:example 的类型推断为"not-allowed" | "open" | "closed"。显然,正确的类型推断应该是状态机只有"open" | "closed"这两种类型,而initial = "not-allowed"要抛错。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容