TypeScript深度思考:一个TodoList项目教会你的不仅是语法 📅 发布时间:2026/7/9 14:28:36 👁️ 浏览次数: 引言在学习 TypeScript 的初期很多开发者会陷入一个误区认为 TS 只是给变量加了个“后缀”比如: string。然而当你真正接手一个中后台项目或者像文中这样的 TodoList 实战时你会发现 TS 的灵魂在于定义契约Contract。这个 TodoList 项目虽然功能简单但它完美地涵盖了数据定义、状态管理、本地存储和组件通信这四大核心场景。通过分析这段代码我们将一起揭开 TS 在实际开发中的面纱。数据契约的基石 —— Interface 与 Type场景重现在项目中我们需要管理待办事项Todo。在 JS 中我们直接用对象但在 TS 中我们必须先定义这个对象“长什么样”。代码解析// 定义数据状态的接口 export interface Todo { id: number; title: string; completed: boolean; }核心干货Interface vs Type虽然两者在定义对象时非常相似但在这个场景下interface是首选。因为interface支持声明合并Declaration Merging。如果未来你需要扩展这个Todo比如增加一个priority字段你可以通过重新声明interface来扩展它而不需要修改原始定义。必选与可选注意这里的属性都是必填的。如果你的 Todo 数据中某些字段可能不存在记得加上?例如description?: string。答疑解惑Q: 为什么一定要定义这个接口直接用any不行吗A: 看起来定义接口增加了代码量但它带来了巨大的收益智能提示IntelliSense当你写todo.的时候编辑器会立刻提示你有id、title、completed可选而不是让你盲打。编译时检查如果你不小心写了todo.nameTS 会在编译阶段报错而不是等到运行时报undefined。泛型Generics的魔法 —— 摆脱 any场景重现在处理localStorage时我们面临一个经典问题localStorage只能存字符串读取时需要反序列化。如果不用泛型我们很容易写出any。代码解析// T 类型参数 类型参数 export function getStorageT(key: string, defaultValue: T): T { const value localStorage.getItem(key); return value ? JSON.parse(value) : defaultValue; }核心干货泛型的本质这里的T就像一个占位符。当你调用getStorageTodo[](todos, [])时TS 会自动推断出这个函数的返回值是Todo[]类型。类型守卫通过泛型我们保证了输入什么类型就输出什么类型。这比写死return JSON.parse(value) as Todo[]更加安全因为这个函数可以复用于任何类型User、Config 等。答疑解惑Q: 为什么这里不直接写JSON.parse(value) as Todo[]A: 因为getStorage是一个通用工具函数。如果写死了Todo[]下次你想存用户信息User时就必须再写一个getUserStorage。泛型让这个函数拥有了“万能钥匙”的能力是 TS 复用性的最高体现。React 组件通信的类型安全场景重现父子组件通过 Props 传递数据。子组件TodoItem需要接收父组件TodoList传来的数据和方法。代码解析interface Props { todo: Todo; onToggle: (id: number) void; onRemove: (id: number) void; } const TodoItem: React.FCProps ({ todo, onToggle, onRemove }) { ... }核心干货函数类型的定义不要使用Function类型Function只表示“这是一个函数”但你不知道它需要什么参数。使用(id: number) void可以精确约束函数签名。React.FC (Function Component)虽然代码中使用了React.FC但在社区中有一种趋势是不使用React.FC。因为React.FC默认包含了children属性如果你的组件不使用children这反而会造成类型污染。新手建议先掌握这种写法后续再了解PropsWithChildren。答疑解惑Q: 为什么onToggle和onRemove的参数是(id: number)而不是直接传整个todo对象A: 这是 React 性能优化的考量。如果传整个对象子组件依赖的是对象引用。在父组件重新渲染时如果对象是新生成的即使内容没变子组件也会强制更新。只传id基本类型配合useCallback可以更好地控制子组件的重渲染。Hooks 与 初始化逻辑的类型推断场景重现在自定义 HookuseTodos中我们不仅要管理状态还要处理本地存储的读取。代码解析const [todos, setTodos] useStateTodo[]( () getStorageTodo[](STORAGE_KEY, []) );核心干货显式泛型标注虽然 TS 通常能自动推断类型但在useState的初始化函数中显式标注Todo[]是一种良好的防御性编程习惯。这确保了setTodos的参数类型也被锁定为Todo[]。副作用依赖useEffect的依赖项[todos]因为todos有了明确的类型TS 能防止你遗漏依赖或者错误地放入了非原始类型的依赖导致死循环。牛刀小试1. 在getStorage函数中如果localStorage里的数据被篡改了比如变成了字符串而不是数组TS 能检测出来吗考察点TS 的静态类型与运行时安全的区别。参考回答“TS 只在编译时检查无法检测运行时的数据篡改。进阶方案在真实项目中我会结合运行时类型检查库如io-ts或zod。在getStorage内部对JSON.parse的结果进行二次校验如果不符合Todo[]的结构就回退到默认值从而保证程序的健壮性。”2. 在useTodos中为什么要写useStateTodo[]不写泛型行不行考察点类型推断的边界。参考回答“如果不写TS 会根据默认值[]推断出类型为never[]或者根据getStorage的泛型推断。虽然通常能推断正确但显式声明是一种**自文档化Self-documenting**的行为。它明确告诉维护者‘这个 State 必须是 Todo 数组’。这符合 TS 的核心原则——让错误在编码阶段暴露而不是在重构时才发现。”3.import type和普通的import有什么区别你为什么在代码里用了import type考察点ES Module 打包与类型擦除。参考回答import type仅在编译阶段使用不会生成任何 JavaScript 代码会被‘擦除’。使用它的主要目的是防止循环依赖和减少打包体积。如果 A 文件 import 了 B 的类型B 又 import 了 A 的类型使用import type可以打破这种循环因为类型在运行时是不存在的。”结语通过这个 TodoList 项目希望你能明白 TypeScript 不仅仅是“加类型”而是一种思维方式的转变。从“我打算怎么写代码”转变为“我定义了什么规则代码必须遵守规则”。不要害怕报错每一次 TS 的报错提示都是它在教你如何写出更健壮的代码。继续加油
Code 128 条码生成器:支持单条 批量生成,实时预览,一键打印,导出图片与 PDF,适配多场景 大家好,我是大飞哥。日常办公或业务场景中,我们常遇到需要制作条码却依赖复杂软件或在线工具的痛点 —— 单条码生成效率低,批量制作更是繁琐,在线工具又存在隐私泄露风险,而专业设备成本过高。这款Code 128 条码生成器… 2026/7/8 20:11:22
收藏!AI岗位薪资暴涨,企业“抢人”大战白热化,程序员/小白必看 打开招聘软件,AI相关岗位的招聘启事堪称“诚意拉满”,每一条都在直击从业者和求职者的痛点: “年薪50万起,急招AI算法工程师,经验不限可内推!” “应届生起薪25万,五险一金全额缴纳,… 2026/7/9 9:14:13
Java毕设项目:基于springboo的地方废物回收机构管理系统(源码+文档,讲解、调试运行,定制等) 博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am… 2026/7/8 14:42:32
【首发狂欢】Windows 11 26H2 彻底封神!7大重磅新功能直击痛点(附在线升级 + ISO手动安装全教程) 作为微软 2026 年下半年的年度重磅更新,Windows 11 26H2终于正式揭开了神秘面纱!相较于过去一些“挤牙膏”式的维护,26H2 是一次真正直击亿万用户痛点的蜕变。更让人惊喜的是,对于 24H2 或 25H2 的用户而言,本次升级采… 2026/7/9 14:26:04
AD5593R与PIC18F96J94的嵌入式信号处理系统设计 1. AD5593R与PIC18F96J94的硬件协同设计 在嵌入式系统开发中,ADC(模数转换器)和DAC(数模转换器)的组合应用极为常见。AD5593R作为一款高度集成的混合信号IO芯片,与PIC18F96J94这款高性能8位MCU的搭配&#… 2026/7/9 14:24:04
Reddit外链:2026不封号拿Dofollow高权重指南 注册一个账号通常只需要耗费30秒钟。一个新账号要长成能发声的形态至少要熬过45天的静默期。前两周每天的在线时间最好严格控制在15到20分钟这个区间。头三天完全不需要敲击键盘,只需用鼠标翻阅前10个热门讨论。机器判定一个活跃用户的门槛,是每天超过5次… 2026/7/9 14:24:04
计算机毕业设计之基于ssm的中医理疗预约平台的设计与实现 本文介绍了一款使用SSM和Vue开发的中医理疗预约平台,及其设计与实现过程。根据软件工程对软件系统开发定制的规则和标准,详细的介绍了系统的分析与设计过程,并且详细的概括了系统的开发与测试过程。本文的管理系统使用了java进行系统的后端开… 2026/7/9 14:24:04
HarmonyOS WPS Open SDK:关闭回传 URI-FD 与 ResultData 解析实现 在 HarmonyOS 应用中集成 WPS Open SDK 后,OpenFileRequest 负责把沙箱内的文档交给 WPS 进程编辑。编辑结束后,最新文件内容往往落在 WPS 自身沙箱,而非调用方传入的原始路径。若业务需要在关窗后继续上传或持久化,必须在请求对象… 2026/7/9 14:22:03
全新窗口置顶方案:打造Mac高效工作新体验 全新窗口置顶方案:打造Mac高效工作新体验 【免费下载链接】Topit Pin any window to the top of your screen / 在Mac上将你的任何窗口强制置顶 项目地址: https://gitcode.com/gh_mirrors/to/Topit 还在为频繁切换窗口而烦恼吗?Topit是一款专为m… 2026/7/9 14:20:02
机器视觉与PLC集成:轮毂缺陷检测与字符识别误差控制在0.2mm内 机器视觉与PLC集成:轮毂缺陷检测与字符识别误差控制在0.2mm内的技术实现轮毂作为汽车关键零部件,其表面质量直接影响行车安全与美观。传统人工检测效率低且易漏检,而采用机器视觉与PLC集成方案可实现微米级精度检测。本文将深入解析高精度视觉… 2026/7/9 0:01:04
GBase 8a vs MySQL 8.0:ALTER TABLE语法与限制的5点关键差异对比 GBase 8a与MySQL 8.0:ALTER TABLE语法差异深度解析与实战指南1. 两种数据库的ALTER TABLE能力全景对比在数据库架构设计和运维过程中,表结构变更(DDL操作)是不可避免的需求。GBase 8a作为国产分析型数据库代表,与开源M… 2026/7/9 0:03:06
【大数据毕业设计】基于多源旅游数据的景区热度分析与推荐系统的设计与实现 基于 Django 的旅游偏好挖掘与景区推荐系统(源码+文档+远程调试,全bao定制等) 博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am… 2026/7/9 0:05:09
6个月转型AI工程师:实战路径与核心技能 1. 项目概述:6个月转型AI工程师的可行性路径在2023年大模型技术爆发的背景下,AI工程师岗位需求同比增长217%(LinkedIn数据)。不同于传统算法工程师需要3-5年培养周期,现代AI工程师更侧重工程化落地能力。我在硅谷科技公… 2026/7/7 11:26:57
TPAFE0808与PIC18F87K22的多通道信号采集方案 1. 项目背景与核心需求在工业自动化、医疗设备和科研仪器等领域,多通道信号采集与系统监测是基础且关键的技术需求。传统方案往往面临通道数量不足、信号调理复杂、系统集成度低等问题。TPAFE0808作为一款8通道模拟前端芯片,与PIC18F87K22微控制器的组合… 2026/7/8 20:15:17
STC3115与PIC18LF26K80构建高精度电池管理系统 1. STC3115与PIC18LF26K80在电池管理系统中的核心价值在现代电子设备中,电池管理系统(BMS)的重要性不亚于设备的核心处理器。STC3115作为一款高精度电池电量监测IC,与PIC18LF26K80微控制器的组合,构成了一个既能精确监控又能智能管理的完整解… 2026/7/8 14:25:08