解锁 TypeScript 的元编程魔法:从 `extends` 到 `infer` 的条件类型之旅

一、类型的三元表达式

条件类型的语法看起来就像 JavaScript 的三元运算符:

typescript 复制代码
SomeType extends OtherType ? TrueType : FalseType;

解析:

  • SomeType extends OtherType:这是我们的"条件判断"。这里的 extends 关键字不是类的继承,而是泛型里面的条件限定判断
  • ? TrueType:如果条件为真,那么最终的类型就是 TrueType
  • : FalseType:如果条件为假,那么最终的类型就是 FalseType

示例:

typescript 复制代码
// IsNumber<T> 会检查类型 T 是否是 number
type IsNumber<T> = T extends number ? "yes, it's a number" : 'no, not a number';

type Result1 = IsNumber<100>; // "yes, it's a number"
type Result2 = IsNumber<'hello'>; // "no, not a number"

let t: Result1 = "yes, it's a number"; // 只能是这个值
let b: Result2 = 'no, not a number';// 只能是这个值
t = '2323' // Error: 不能将类型""2323""分配给类型""yes, it's a number""。ts(

二、魔法关键字 infer

infer 允许我们在 extends 子句中声明一个待推断的类型变量。如果类型匹配成功,这个变量就会"捕获"对应位置的类型,然后我们可以在条件为真的分支中使用它。

typescript 复制代码
type ReturnType<T> = T extends (...args: any[]) => infer P ? P : T;
interface User {
    name: string;
    age: number;
}
type Func = (name:string) => User;
type pType = ReturnType<Func>; // pType = User
type tType = ReturnType<string>; // tType = string

解析:

  • T 限定判断为一个函数(...args: any[])=> infer P,并且函数的返回类型为P
  • 限定为真时,ReturnType为P
  • 限定为假时,ReturnType为T

三、分布式条件类型

当一个条件类型作用于一个 的泛型参数,且该参数的实际类型是联合类型时,它会被分发到联合类型的每一个成员上进行独立计算,最后将结果联合起来。

typescript 复制代码
type ToArray<T> = T extends string | number ? T[] : T;

type T1 = ToArray<string | number>; // 结果是 string[] | number[]
type T3 = ToArray<boolean>; // 结果是 boolean

type ToArray2<T> = T extends any ? T[] : never;
type T4 = ToArray2<string | number>; // 结果是 string[] | number[]

解析:

  • string | number或者any都能包含 T1T4的联合类型
  • T1T4符合限定,所以返回T[]类型,结果就是分别对联合类型的各个类型进行了数组化处理

例如,我们自己实现一个 Exclude

typescript 复制代码
type MyExclude<T, U> = T extends U ? never : T;

// 'a' | 'b' | 'c' 排除 'a' | 'd'
type Result = MyExclude<'a' | 'b' | 'c', 'a' | 'd'>; // 结果:'b' | 'c'

解析:

  • ('a' extends 'a' | 'd' ? never : 'a') => never
  • ('b' extends 'a' | 'd' ? never : 'b') => 'b'
  • ('c' extends 'a' | 'd' ? never : 'c') => 'c'
  • 最终联合结果:never | 'b' | 'c',即 'b' | 'c'

如何阻止分发? 如果你不希望发生分发,可以用方括号 [] 将泛型参数包裹起来,[T] 就不再是 的泛型参数,不是后就不会分发,而是作为一个整理进行处理:

typescript 复制代码
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;

type T2 = ToArrayNonDist<string | number>; // 结果是 (string | number)[]

总结

如果你喜欢本教程,记得点赞+收藏!关注我获取更多JavaScript/TypeScript开发干货

相关推荐
草字几秒前
uniapp 滚动到表单的某个位置,表单验证失败时。
前端·javascript·uni-app
学到头秃的suhian几秒前
Spring使用三级缓存解决循环依赖问题
前端·spring·缓存
CXH7282 分钟前
架构师的登山之路|第十二站:服务网格 Istio——未来的标配,还是复杂过头?
前端·javascript·istio
脾气有点小暴24 分钟前
详解 HTML Image 的 mode 属性:图像显示模式的灵活控制
前端·html·uniapp
前端之虎陈随易25 分钟前
基于Go重写的TypeScript 7可以用了
开发语言·golang·typescript
爱吃无爪鱼38 分钟前
03-Bun vs Node.js:JavaScript 运行时的新旧之争
javascript·vue.js·react.js·npm·node.js
0思必得01 小时前
[Web自动化] 开发者工具性能(Performance)面板
运维·前端·自动化·web自动化·开发者工具
心灵的制造商1 小时前
el-tree左侧新增类别和删除类别实例代码
前端·javascript·vue.js
爱吃无爪鱼1 小时前
01-前端开发快速入门路线图
javascript·css·vue.js·typescript·前端框架·npm·node.js
冴羽1 小时前
不知道怎么写 Nano Banana Pro 提示词?分享你一个结构化示例,复刻任意图片
前端·人工智能·aigc