TypeScript知识体系:类型系统、工具类型与tsconfig实战
最近 TypeScript 社区最大的新闻就是 7.0 打算把baseUrl和moduleResolution: node10两个老配置项直接移除。很多同事的第一反应是我的 tsconfig 是不是要炸第二反应才是这俩选项到底是干嘛的。恰好面试市场也卷得厉害从工具类型推导到模块解析考的全是理解层面不是背几个语法。我用 TypeScript 从搭脚手架到封库再到线上排错这几年最深的感受就是知识体系不是靠记 API 堆起来的而是把类型系统、配置体系、工程约束三条线串起来之后自然长出来的。这篇文章不打算面面俱到只挑最高频、最容易踩坑的部分按我自己的认知顺序过一遍新手能建立全局观老手能查漏补缺。1. 先搭认知框架TypeScript 知识体系的三条主线1.1 类型系统不是给 JS 加约束而是另一门编程语言很多人学 TypeScript 的误区是把它当成JavaScript 加类型注解。实际上类型系统本身就是一套完整的、图灵完备的小型语言它可以做判断、做映射、做递归、做条件分支——这些能力被称为类型编程。举个例子下面这个类型定义能根据传入的布尔值决定返回值类型type ExampleT extends boolean T extends true ? string : number;这不只是给变量标个类型而是让类型参与到逻辑计算里。理解这一点才算真正进入 TypeScript 的深度领域。日常写业务可能用不到多复杂的类型体操但读组件库、读框架源码、处理第三方类型定义时这层认知就是分水岭。1.2 配置体系tsconfig 里藏着项目的一半真相知识体系的第二条线是 tsconfig。我见过很多项目报了错先查代码查半天发现是编译目标配错或者模块解析方式不对。target、module、strict、moduleResolution这几个选项决定了代码怎么被编译、模块怎么被找到、类型检查严格到什么程度。最近社区热议的baseUrl弃用就是典型例子。baseUrl原本用来设置非相对路径导入的基准目录配合paths做路径别名。但从 TypeScript 5.x 开始官方逐步推荐直接用paths配合相对路径不再需要baseUrl。7.0 一旦移除老项目不迁移就会直接编译失败。1.3 工程约束从类型定义到模块解析都在为协作服务第三条主线是工程化。TypeScript 不只是编译工具它还是团队协作的契约层。.d.ts类型声明文件就是你和团队、你和第三方库之间签订的接口协议extends配置让不同项目共享一套约束声明合并让你能在保证类型安全的前提下扩展现有 API。这三条线不是割裂的类型系统解决数据长什么样配置体系解决代码怎么跑工程约束解决人和人怎么协作。这篇文章的后续章节基本都是围绕这三条线展开先把框架立起来后面填充细节就不容易乱。2. 类型系统的地基从基础类型到泛型把类型当成值来思考2.1 类型是内存里那堆数据的边界协议我习惯把类型理解为两个模块之间的边界协议A 模块说我返回一个对象里面有name和ageB 模块接过来就能安全地用这两个字段。这种协议在编译期生效运行时只是被擦除所以它不增加任何内存负担但能拦下大量低级错误。TypeScript 的基础类型比 JavaScript 多一层复合的概念除了string、number、boolean等原始类型还有数组、元组、对象字面量、联合类型、字面量类型。实际项目里最常见的错误发生在打错字段名和漏判空值上开启strict: true之后这类错误会在写代码那一刻就被暴露出来interface User { name: string; age?: number; } function sayHello(user: User) { console.log(user.name.toUpperCase()); console.log(user.age.toString()); // 报错user.age 可能为 undefined }2.2 interface 与 type 的选择从来不是玄学interface和type都能定义对象结构区别在于interface支持声明合并后面多次声明同名接口会自动合并type可以定义联合类型、交叉类型、工具类型这类计算后的结果。社区里能用 interface 就用 interface需要计算才用 type的建议本质是因为interface的错误提示往往更直观合并能力也让扩展第三方声明更方便。实际开发里我自己的选择标准是描述 API 响应的形状、组件 Props 这类固定结构用interface要基于现有类型做变换比如type PartialUser PartialUser或者定义联合类型时用type。这个原则能减少大部分纠结。2.3 泛型就是类型的函数我给新同事讲泛型时最爱用这个类比函数是把值当参数返回值泛型是把类型当参数返回类型。比如写一个缓存函数function cacheT(key: string, value: T): T { const store new Mapstring, T(); store.set(key, value); return store.get(key)!; } const num cachenumber(x, 42); // number const str cache(y, hello); // 自动推断 string泛型让组件和函数通吃多种类型同时保持类型关系不被破坏。这几乎是所有高级类型技巧的基石映射类型、条件类型都依赖泛型参数。面试里问到泛型有什么用答出保持类型约束的同时复用逻辑基本就过关。2.4 联合类型、字面量类型与类型收窄联合类型用|表示可能取这些类型之一字面量类型让success | error这样的字符串成为独立类型。两者结合再加上类型收窄是处理有界状态机的核心武器type Result | { status: success; data: string } | { status: error; error: Error }; function handle(r: Result) { if (r.status success) { console.log(r.data); // 这里确定有 data } else { console.log(r.error.message); // 这里确定有 error } }收窄的本质是利用判别字段discriminant在分支内压缩可能性。实际代码里接口返回的code字段、组件状态机的state字段都很适合用这种模式建模。3. 常用内置工具类型逐个拆解这些 API 是面试和实战的双重高频3.1 映射类工具Partial、Required、Readonly、Pick、Omit这是使用频率最高的一组工具类型。它们都基于映射类型实现——遍历一个对象类型的每个键对其值类型做变换type PartialT { [P in keyof T]?: T[P] }; type RequiredT { [P in keyof T]-?: T[P] }; type ReadonlyT { readonly [P in keyof T]: T[P] }; type PickT, K extends keyof T { [P in K]: T[P] }; type OmitT, K extends keyof T PickT, Excludekeyof T, K;Partial把每个属性变成可选Required反向操作Readonly加只读标记。Pick从类型里挑出若干键组成新类型Omit是去掉若干键。日常场景里表单编辑一个大对象时用PartialEntity列表接口只返回部分字段时用PickEntity, id | name都在疯狂复用它们。3.2 条件类工具Exclude、Extract、NonNullable条件类型T extends U ? X : Y是类型系统的三元表达式。基于它官方提供了一组筛选工具type ExcludeT, U T extends U ? never : T; type ExtractT, U T extends U ? T : never; type NonNullableT T extends null | undefined ? never : T;Exclude做差集Extract做交集NonNullable剔除空值。它们的典型用法是配合联合类型做过滤。比如Excludea | b | c, a得到b | c。在定义路由白名单、过滤事件名这类场景里非常实用。3.3 函数推断类工具Parameters、ReturnType、Awaited这一组专门从函数类型里提取信息type ParametersT extends (...args: any) any T extends (...args: infer P) any ? P : never; type ReturnTypeT extends (...args: any) any T extends (...args: any) infer R ? R : never;Parameterstypeof fn拿到函数参数元组ReturnTypetypeof fn拿到函数返回类型AwaitedT递归解包 Promise。这里的核心操作符是infer它在条件类型里声明一个待推导的类型变量。写装饰器、写复杂 Hooks 的类型签名时靠它们能省掉大量重复书写。3.4 字符串字面量工具Uppercase、Lowercase、CapitalizeTypeScript 4.1 引入的模板字面量类型配合这套大小写工具可以推导事件名、路由前缀这类字符串结构type EventPrefix on${Capitalizeclick | scroll}; // 结果是 onClick | onScroll配合infer还能做字符串解析比如从user:123里拆出id的字符串部分。这类技巧在写类型安全的查询参数解析、事件总线时是杀手锏。表格总结一下常用工具类型的用途工具类型作用实现要点PartialT所有属性可选映射类型加?RequiredT所有属性必选映射类型加-?ReadonlyT所有属性只读映射类型加readonlyPickT, K挑出 K 键[P in K]OmitT, K排除 K 键PickT, Excludekeyof T, KExcludeT, U从 T 中排除 U条件类型返回neverExtractT, U从 T 中提取 U条件类型返回本身ReturnTypeT函数返回类型infer RParametersT函数参数元组infer PAwaitedT递归解包 Promise递归条件类型3.5 一张表看清内置类型的内部实现把内置类型的实现读一遍比背十遍文档都有用。你会在里面看到keyof、in、extends、infer这些操作符如何被组合理解了它们自己写工具类型就不再是照着抄而是组合逻辑。比如Omit的实现用到了Exclude而Exclude又用到了条件类型。这说明官方工具类型是有层次的设计先打好条件类型和映射类型的基础再看这些实现就是顺水推舟。4. tsconfig 核心配置与 7.0 弃用项迁移别等报错再焦虑4.1 一个能跑的 tsconfig 最少需要什么新手最容易把 tsconfig 抄成一大坨其实项目能跑得舒服最核心的配置项不超过一页{ compilerOptions: { target: ES2020, module: ESNext, moduleResolution: bundler, strict: true, jsx: react-jsx, skipLibCheck: true, esModuleInterop: true, resolveJsonModule: true, strictNullChecks: true }, include: [src] }target决定输出语法的 ES 版本module决定代码模块化格式strict是总开关moduleResolution在 5.x 之后已经支持bundler专门给 Vite、webpack 这类打包器用。这套配置能用在全前端项目上后端 Node.js 项目把module换成NodeNext即可。4.2 baseUrl 弃用用 paths 相对路径替代baseUrl原本解决的是从任意位置以同一基准路径导入的痛点。比如设定baseUrl: ./src之后import { utils } from /utils就能从src目录下找文件。问题在于baseUrl语义模糊而且和打包器里的别名机制容易冲突所以官方决定在 7.0 移除它。迁移思路非常直接不再设置baseUrl只用paths并写成相对路径{ compilerOptions: { paths: { /*: [./src/*] } } }注意paths的写法从[src/*]变成[./src/*]加了./前缀。这样行为与原来一致又避开了baseUrl依赖。如果你还用了paths但没写./升级前一定要补上这是最常见的迁移报错点。4.3 moduleResolution: node10 退出历史舞台node10原node是历史上最早用于 CommonJS 时代 Node.js 的解析策略规则老、不识别现代exports条件导出。7.0 移除后官方主推两条路bundler和NodeNext。前端打包器项目选bundler原生 Node.js 项目选NodeNext。我最近升级一个老库时就遇到moduleResolution: node10导致的类型找不到问题依赖包已经用exports字段声明导出但node10压根不认直接从根路径找结果报 TS2307。切到bundler之后一切正常。所以提前把你的 tsconfig 换到新策略别等项目报错再临时抱佛脚。4.4 升级前自查清单[ ] 检查 tsconfigpaths是否都以./开头[ ] 删除baseUrl字段用相对路径重写paths[ ]moduleResolution切换为bundler或NodeNext[ ] 跑一遍npx tsc --noEmit确认无类型错误[ ] 检查第三方依赖的types入口是否兼容新解析规则[ ] 用npx typescript-eslint/parser顺带排查 import 顺序问题升级本身不大但每个老项目总有一两个隐藏的坑。核心原则是先保证路径别名能生效再管 module 解析最后用编译检查兜底。5. 面试高频类型体操与 API 场景题会写不算会能讲清才是会5.1 从手写 Partial 理解映射类型的本质面试里让手写Partial考察的其实是两个点知道keyof得到键的联合类型知道[P in keyof T]是遍历键并生成对应类型的映射语法。一个干净的实现如下type MyPartialT { [P in keyof T]?: T[P] };答完之后最好主动提一句加上-?就是Required的效果加上readonly前缀就是Readonly的效果。这一下就展示了你还理解同一族工具的整体关系。5.2 DeepReadonly递归条件类型的经典练习DeepReadonly要求把嵌套对象的每一层都锁成只读标准答案关键是数组和对象要递归处理type DeepReadonlyT T extends (infer R)[] ? ReadonlyArrayDeepReadonlyR : T extends object ? { readonly [K in keyof T]: DeepReadonlyT[K] } : T;这里用infer R先解出数组元素类型再递归包装。面试官看到你主动处理数组和原始类型的边界就知道你真的理解递归条件类型而不是背答案。5.3 类型安全的 API 客户端该怎么设计比纯体操更贴近实际的是类型安全 API 客户端设计题。这类题通常给一个request函数让你保证传进去的路径和对应的返回类型是匹配的type ApiMap { /user: { id: number; name: string }; /user/list: Array{ id: number; name: string }; }; async function getTKey extends keyof ApiMap( path: TKey ): PromiseApiMap[TKey] { const res await fetch(path); return res.json(); } const user await get(/user); // 自动推断 { id: number; name: string }这里的核心思想是路径字面量类型作为映射索引把路由表和响应类型绑定在一起。考这个题目比让候选人默写工具类型更能看出他在真实工程里有没有类型设计意识。5.4 面试答题的逻辑顺序回答 TypeScript 面试题我总结的经验是先给结论再给机制最后给例子。比如问interface和type的区别先一句话给出interface 可合并type 可计算然后解释合并能力来源和类型计算场景最后贴一小段代码对比。这比上来就长篇大论的效果好面试官能在 30 秒内抓到你的理解深度。另外要学会讲清楚为什么比如为什么exactOptionalPropertyTypes影响可选属性赋值因为这会让?: string不再接受undefined。这类边角配置反而最能体现功力。6. 实战中的 API 类型设计与错误排查经验6.1 RESTful 响应的类型建模成功与失败要分开线上项目最常见的问题是后端接口的响应结构不统一。有的返回{ code: 0, data: ... }有的直接返回data失败的字段名也是五花八门。我现在的做法是不管后端多混乱前端一律用判别联合类型建模interface ApiSuccessT { code: 0; data: T; message: ok; } interface ApiFailure { code: number; error: string; detail?: Recordstring, unknown; } type ApiResponseT ApiSuccessT | ApiFailure;这样在fetch之后必须收窄到code 0才能拿data而错误分支可以统一处理message和detail不会出现某个接口在职员列表里没有error字段导致线上炸掉的事故。6.2 异步接口的错误联合类型与收窄处理 Promise 场景时我更推荐用一个自定义的Result联合类型显式表达错误type ResultT, E Error | { ok: true; data: T } | { ok: false; error: E }; async function safeFetchT(url: string): PromiseResultT { try { const res await fetch(url); const data (await res.json()) as T; return { ok: true, data }; } catch (e) { return { ok: false, error: e as Error }; } }调用方会在收窄时被强制处理ok: false分支比直接try/catch散落各处优雅得多错误处理也统一。这在封装大模型 API、上报监控这类对外部依赖较多的场景尤其有用。6.3 常见编译错误的排查路径下面是我在实际项目里遇到最多、也最值得储备的几类错误各配一句排查思路报错代码含义排查切入点TS2307找不到模块先查路径别名配置再查moduleResolution是否兼容依赖的exports字段TS2339属性不存在于该类型确认类型收窄是否完成或联合类型分支是否有判别字段TS2322类型不可赋值使用type和interface交叉对比检查可选属性是否影响TS2531对象可能为 null开启strictNullChecks后必须显式判空或使用非空断言TS2580找不到require声明缺少types/node或模块系统配置不当经验之谈报错代码是关键线索别只看红色波浪线的文字描述。先把tsc --noEmit的完整错误列表跑出来按数量最多的类型优先处理效率最高。6.4 环境切换与类型体操的边界最后聊一点个人体会类型体操能力再强也要克制。项目里 80% 的类型需求靠官方工具类型加上 3-4 个自定义泛型就足够。复杂体操容易把代码写难读评审时团队看不懂后果很严重。我给自己定的规则是类型逻辑打超过三层递归就要抽成独立类型必加注释说明输入输出。另一个真实场景是环境切换本地跑和线上跑导入路径可能不同千万别把类型逻辑里混入运行时环境判断。类型无法感知环境变量一旦混入影响的是编译产物排查起来极其痛苦。保持类型纯粹描述数据其他交给运行时逻辑。我把 TypeScript 当与人交流的设计文档来用而不是给编译器的限制条件。类型写清楚团队协作才能顺畅这也是知识体系里最有价值、最不需要炫技的那部分。

相关新闻

从目录到加载:WordPress块主题Twenty Twenty-Five全解析

从目录到加载:WordPress块主题Twenty Twenty-Five全解析

从二十多年前的经典主题一路用过来,再回头看 WordPress 6.7 默认带的 Twenty Twenty-Five,我最大的感受是:做主题这件事,已经从“写 PHP 模板”变成了“组织一份设计系统”。TT5 是官方默认主题里最新的块主题样本,目录…

2026/10/12 8:36:13 阅读更多 →
影刀RPA实战指南:从流程自动化到职业竞争力提升

影刀RPA实战指南:从流程自动化到职业竞争力提升

去年年底,我们部门接了个紧急任务:每天要从三个系统里导出数据、按客户维度填表、再通过邮件回传,加起来四十多个步骤,手动操作一整天才能跑完。我原以为这活儿得干到年终,没想到花了两个周末把整个流程用影刀RPA串了起…

2026/10/12 8:36:13 阅读更多 →
内部训练赛题解:从字符串处理到状态压缩BFS的算法进阶

内部训练赛题解:从字符串处理到状态压缩BFS的算法进阶

程序设计工作室1月28日内部训练赛 题解 先说一下整体情况。这次内部训练赛一共6道题,定位是给工作室春季招新前做一次全员摸底,难度从签到到压轴逐步拉高,覆盖了字符串处理、前缀和、二分答案、背包DP、带约束线性DP、状态压缩BFS这些竞赛里…

2026/10/12 8:35:08 阅读更多 →

最新新闻

Activiti并行网关详解:BPMN设计、XML配置与并发执行避坑指南

Activiti并行网关详解:BPMN设计、XML配置与并发执行避坑指南

工作流引擎里若是排一个“让人又爱又恨的节点榜”,并行网关大概率能进前三。爱它,是因为它能一次把流程拆成多条并发分支,几个没有先后依赖的任务同时跑,整体耗时从“几个任务之和”变成“最慢的那条分支”;恨它&#…

2026/10/12 19:55:13 阅读更多 →
Cursor 设置中文回复:把 Base URL 改到 TaoToken 后界面仍英文的排查清单

Cursor 设置中文回复:把 Base URL 改到 TaoToken 后界面仍英文的排查清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 19:55:13 阅读更多 →
Java爬虫实战:用Jsoup+Selenium抓取动态短视频页面数据

Java爬虫实战:用Jsoup+Selenium抓取动态短视频页面数据

先说结论:这个项目标题里提到的“西瓜小视频”,我在这里把它当成一个纯演示用的虚构站点,数据也都是模拟数据,不特指任何真实平台。整篇文章分享的是用 Java 配合 Jsoup 和 Selenium 抓取一个动态渲染的短视频列表页,把…

2026/10/12 19:55:13 阅读更多 →
Invoice Builder报价单完全指南:从创建报价到一键转发票

Invoice Builder报价单完全指南:从创建报价到一键转发票

【免费下载链接】invoice-builder Invoice and quotation builder desktop app with PDF export, designed for small businesses and freelancers. Create, manage, and export invoices and quotes easily using a local database in an Electron-based app. 项目地址&#x…

2026/10/12 19:55:13 阅读更多 →
生态观察:Laya 刷榜一周,中文社区的教程、微调、部署内容正在全面开花

生态观察:Laya 刷榜一周,中文社区的教程、微调、部署内容正在全面开花

生态观察:Laya 刷榜一周,中文社区的教程、微调、部署内容正在全面开花 【免费下载链接】laya 项目地址: https://ai.gitcode.com/hf_mirrors/convaiinnovations/laya 开源一周,Laya 在中文技术社区的内容密度已经远超多数同类项目一个…

2026/10/12 19:55:13 阅读更多 →
AnyPS5项目解析:从命名歧义看技术代号规范的重要性

AnyPS5项目解析:从命名歧义看技术代号规范的重要性

项目标题为“AnyPS5”,但提供的输入内容中,项目正文、关键词、摘要描述等核心字段全部为空,相关热搜词与网络搜索内容也未给出实质性信息(仅显示空代码块)。在无任何有效上下文、领域指向、功能描述或技术线索的前提下…

2026/10/12 19:54:12 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →