当前位置: 首页 > news >正文

【Typescript】07-泛型入门与实战

泛型入门与实战

很多人第一次看到泛型,都会觉得它有点抽象。代码里突然多出一个TKV,好像进入了另一个层级。其实泛型的本质非常朴素:让类型也能像函数参数一样被传入。

如果说前面的类型声明解决的是“这个东西是什么”,那么泛型要解决的是“这段逻辑对很多种类型都成立,但我仍然希望保留它们之间的关系”。它不是一种炫技工具,而是 TypeScript 从静态标注走向类型抽象的关键一跃。

为什么我们需要泛型

先看一个没有泛型的例子:

functiongetFirstAny(list:any[]){returnlist[0];}

这段代码当然能工作,但问题很明显:你传入的是字符串数组也好,用户对象数组也好,返回值都会被推成any。后面的类型信息直接断掉。

再看泛型版本:

functiongetFirst<T>(list:T[]):T{returnlist[0];}

如果你传入string[],返回值就是string;传入number[],返回值就是number;传入User[],返回值就是User

这就是泛型真正有价值的地方:它保留了输入和输出之间的类型关系。

最简单的泛型函数

functionidentity<T>(value:T):T{returnvalue;}

这里的T就是类型参数。它不是具体类型,而是一个“稍后再决定”的类型占位符。

调用时你可以显式写出:

consta=identity<string>("hello");

也可以交给 TypeScript 推断:

constb=identity(123);

大多数情况下,推断已经足够好。你真正需要关心的,是T在函数内部和外部之间建立了什么关系。

泛型不是为了灵活,而是为了“带约束地灵活”

这一点非常关键。很多人会觉得“反正泛型就是更灵活”,但真正准确的理解应该是:泛型让代码在保持类型安全的前提下获得灵活性。

它和any最大的区别就是:

  • any会抹掉关系
  • 泛型会保留关系

所以泛型不是any的高级版,而是另一种完全不同的思想。

一个更接近实际的例子:包装返回值

functionwrap<T>(value:T){return{value};}

当你调用:

constwrappedUser=wrap({id:1,name:"Alice"});

TypeScript 会自动推断出:

{value:{id:number;name:string;};}

这类模式在请求封装、状态管理、缓存系统、表单工具里非常常见。

泛型约束:不是任意类型都可以

有时候你不是想要“任何类型都能进来”,而是“任何满足某个条件的类型都能进来”。这时就要用约束。

functionprintLength<Textends{length:number}>(value:T){console.log(value.length);}

这里的extends不是类继承,而是说:T至少要具备length: number这个结构。

因此下面这些都可以:

printLength("hello");printLength([1,2,3]);

但下面这种就不行:

printLength(123);

因为数字没有length

泛型约束解决的是“抽象不能脱离现实”

很多初学者一接触泛型,就会把它理解成“无限自由的占位符”。这其实容易走偏。泛型真正成熟的用法,往往不是无限泛,而是在抽象中保留必要边界

比如你做一个排序函数,可能只接受可比较的值;你做一个列表函数,可能要求元素具备某个主键;你做一个缓存函数,可能要求 key 是字符串。这些都是泛型约束的用武之地。

泛型接口与泛型类型别名

泛型不仅能写在函数上,也能写在接口和类型别名上:

interfaceApiResponse<T>{code:number;data:T;message:string;}typePageResult<T>={list:T[];total:number;};

这两种定义在真实项目里极其常见。比如:

  • ApiResponse<User>
  • ApiResponse<Order[]>
  • PageResult<Article>
  • PageResult<Comment>

它们的共同点是:整体结构固定,只有其中某一部分随场景变化。

分页接口为什么天然适合泛型

因为分页结果通常长这样:

typePageResult<T>={list:T[];total:number;page:number;pageSize:number;};

不管你分页的是用户、订单还是文章,分页外壳都是一样的,变化的只是list里的元素类型。这个“固定外壳 + 可变化内容”的结构,几乎就是泛型最标准的使用场景。

多个类型参数:不是为了复杂,而是为了表达多段关系

functionmapToObject<Kextendsstring,V>(key:K,value:V):Record<K,V>{return{[key]:value}asRecord<K,V>;}

这里同时用了两个类型参数:

  • K表示键
  • V表示值

这不是为了炫技,而是因为这里确实有两套不同的类型关系要表达。

你应该把多个类型参数理解成“多条关系线”,而不是“语法更高级”。一旦函数里涉及多个位置、多个输入、多个输出之间的对应关系,多个类型参数就很自然。

泛型默认类型

再进一点,你还会看到这种写法:

typeApiResponse<T=unknown>={code:number;data:T;message:string;};

这里T = unknown表示如果调用方没有显式指定类型参数,就默认用unknown。这种写法在库设计里很常见,能在灵活性和安全性之间提供一个更稳妥的默认值。

泛型类也存在,但别过度使用

classBox<T>{constructor(publicvalue:T){}getValue():T{returnthis.value;}}

泛型类并不少见,但在业务代码里,泛型函数和泛型类型通常比泛型类更常用。原因也很简单:很多场景只需要表达输入输出关系,不一定需要实例化对象。

什么时候不该用泛型

这是一个特别重要的判断能力。不是只要能写成泛型,就应该写成泛型。

如果一个函数只处理一种固定类型,例如:

functionformatPrice(price:number):string{return`¥${price.toFixed(2)}`;}

那就没必要硬改成泛型。很多初学者一学会T,就想把所有函数都写成泛型,结果代码里到处都是没有实际意义的类型参数。

好的泛型通常满足两个条件:

  • 这段逻辑确实对多种类型都成立
  • 类型参数能表达明确、可复用的关系

初学者常见误区

误区一:把泛型当作“高级的 any”

泛型不是给你偷懒用的,它是为了保留关系,而不是为了逃避具体类型。

误区二:看见可复用就立刻上泛型

如果抽象层次太早,类型参数名会满天飞,反而让代码更难读。泛型应该解决真实重复,而不是想象中的未来复用。

误区三:类型参数命名混乱

简单场景用TKV没问题,但一旦语义明显,命名更具体往往更好,比如TDataTItemTKey。可读性比“写得短”更重要。

本文小结

泛型不是让代码更复杂,而是让可复用逻辑在不丢失类型关系的前提下保持灵活。它解决的核心问题不是“这个值是什么”,而是“当输入变化时,输出应该如何随之变化”。

真正掌握泛型之后,你看待类型系统的方式会发生变化。你不再只是给现有结构贴标签,而是开始设计类型之间的关系网。这也是 TypeScript 从“会写标注”走向“会做抽象”的关键一步。

练习

  1. 写一个泛型函数wrap<T>,返回{ value: T },并分别传入字符串、数字和对象测试推断结果。
  2. 写一个泛型类型State<T>,包含loadingdataerror三个字段,并为用户列表和文章列表分别实例化。
  3. 思考:分页接口、缓存容器、请求响应包装器为什么几乎天然适合用泛型?

后记

2026年5月21日于上海。

http://www.jsqmd.com/news/859779/

相关文章:

  • RPC 核心概念 04:服务发现与负载均衡
  • 通过Taotoken的审计日志功能追踪团队内部的大模型API调用情况
  • ComfyUI InstantID:让AI真正记住你的脸,创作独一无二的数字分身
  • 5步解决Chrome浏览器密码管理难题:ChromeKeePass实现KeePass自动填充
  • 知识竞赛加赛规则:平分决胜的三种方案
  • 突破性解决方案:Unity开发者如何告别命令行Git的繁琐操作?
  • 如何免费解决BT下载速度慢问题?终极trackerslist配置指南
  • 微信聊天记录导出完整指南:无需越狱永久保存你的珍贵对话
  • 气缸机 vs 气囊机怎么选?2026 中立客观拆解:别再纠结效果,核心看长期稳定性
  • 终极指南:3种Python方法免费获取百度网盘高速下载直链
  • Git-Sim终极指南:可视化模拟Git操作的完整教程
  • 信创验收避坑指南:从一份紧急的补充材料,谈合规检测的必要性
  • SketchBook Pro 中文版
  • 二叉树的序列化与反序列化详解
  • 2026 在线考试系统哪个好?功能、客户、方案、优势与服务全对比
  • ElevenLabs潮州话语音接入全链路方案(含潮汕八邑口音适配白皮书)
  • 操作简便吗?8款一键生成论文工具梯队榜,毕业护航!
  • 初次接入Taotoken,从注册到发出第一个请求的全流程耗时
  • 2026 科技改变财税:税慧盟,构建智能财税新生态 - 品牌企业智选官
  • ElevenLabs老挝文语音效果翻倍的3个隐藏参数:声调补偿权重、SIL分段阈值、Lao orthographic normalization开关(内部测试版配置文件限时放送)
  • 当“数字孪生”有了坐标、时序和一棵“会落叶的树”:NNU‑Campus‑Geo3DGS 数据集深度解读
  • 2025 年欧美明星人形机器人企业接连倒闭,中国企业融资却屡创新高,赛道冰火两重天!
  • 如何3步免费下载百度文库文档:PDF保存终极指南
  • ElevenLabs湖北话语音API调用性能暴跌47%?这才是真实原因——Nginx代理配置+方言token缓存策略深度优化方案
  • 国内主流燕窝线上品牌实测排行 品质与性价比对比 - 互联网科技品牌测评
  • Fastboot Enhance:如何通过图形化界面高效管理Android设备分区与Payload文件?
  • AI时代,那些还在知乎认真回答问题的人
  • 使用Taotoken CLI工具一键为团队所有网站项目配置统一API接入点
  • 利用 QiWe API 实现企业微信机器人消息双向交互
  • CANN-ops-math推理优化-昇腾NPU数学算子调优实战