- 文档
- 教程
【免费下载链接】TypeScript
TypeScript 使用手册(中文版)翻译。http://www.typescriptlang.org
本文聚焦 TypeScript 2.6 版本引入的两项破坏性改动:
--noUnusedLocals/--noUnusedParameters对"只写不读"声明的全新检测逻辑,以及环境模块(ambient module)声明文件中禁止任意表达式作为导出值的新规则。阅读本文后,你将理解这两项改动的触发条件、报错信息、正确的迁移写法,以及在升级到 TypeScript 2.6 及以上版本时如何快速定位和修复受影响的代码。
只写引用(Write-only)现在会被标记为未使用
TypeScript 2.6 修复了--noUnusedLocals和--noUnusedParameters两个编译选项的实现逻辑。在此之前,这两个选项只检查声明是否被"引用"(reference);从 2.6 开始,它们检查的是声明的值是否被"读取"(read)。任何只被写入、却从未被读取的声明,现在都会被标记为未使用。
选项的语义与默认值
在本仓库的编译选项文档中,这两个选项的定义如下:
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
--noUnusedLocals | boolean | false | 若有未使用的局部变量则抛错。 |
--noUnusedParameters | boolean | false | 若有未使用的参数则抛错。 |
两个选项默认均为关闭状态,可以在命令行或tsconfig.json中显式开启。
触发条件:只写不读的代码
下面这段代码在 TypeScript 2.5 及更早版本中不会产生编译错误,但在启用--noUnusedLocals和--noUnusedParameters的 TypeScript 2.6 中会被标记为未使用:
function f(n: number) { n = 0; } class C { private m: number; constructor() { this.m = 0; } }分析这段代码:
- 函数
f的参数n只被赋值(n = 0),其值从未在任何地方被读取,因此触发--noUnusedParameters报错; - 类
C的私有属性m同样只被写入(this.m = 0),从未被读取,因此触发--noUnusedLocals报错。
注意:在 TypeScript 2.6 中,类私有属性的未使用检查归入--noUnusedLocals的管辖范围,因为对于该检查器而言,它们属于"局部"的声明。
这一改动使检查语义从"是否被引用"收紧为"是否被读取":引用(如赋值)不再足以让声明免于未使用报错,只有真正的读取(如参与运算、作为参数传递、被返回等)才能让声明被视为"已使用"。
仅自调用的递归函数被视为未使用
另一个值得注意的边界情况是递归函数。仅在自己的函数体内被调用的递归函数,现在也被视为未使用:
function f() { f(); // Error: 'f' is declared but its value is never read }函数f唯一的"使用"发生在它自身内部,从外部视角看,这个声明依然没有任何地方读取它的值,因此被标记为未使用。
这一规则与发布说明中描述的"只写的引用现在会被标记未使用"一致:只要声明的值在程序其他部分从未被消费,无论它内部如何自我引用,都会被视为死代码。
修复建议
受影响的代码通常可以通过以下方式修复:
- 确实无用的声明直接删除:对于只写不读的参数或局部变量,如果确实不需要,直接删除即可;
- 以下划线开头的参数:对于需要保留签名(如实现接口、回调函数)但未使用的参数,使用下划线前缀(如
_n)是社区通行的约定,TypeScript 对这类参数不会报未使用错误; - 为属性补充读取点:如果类属性确实需要在后续逻辑中使用,补上读取它的代码即可消除报错。
后续演进:TypeScript 2.8 的调整
需要留意的是,这组选项的检查范围在后续版本仍在演进。本仓库的TypeScript 2.8 破坏性改动文档指出:未使用的类型参数此前在--noUnusedLocals下报告,从 2.8 起改由--noUnusedParameters报告。也就是说,"只读检查"的语义框架在 2.6 建立后,后续版本对报告归属做了进一步细化,升级到 2.8 及以上版本时需要关注这一归类变化。
环境上下文中的导出赋值禁止使用任意表达式
第二项破坏性改动针对环境上下文(ambient context),即声明文件(.d.ts)与declare module环境模块声明中的导出语句。
改动前:任意表达式曾被允许
在 TypeScript 2.5 及更早版本中,下面的写法在环境上下文中不会被标记为错误:
declare module 'foo' { export default 'some' + 'string'; }为什么被禁止
声明文件和环境模块通常禁止出现表达式,原因在于typeof之类的意图不明确:环境声明只描述类型形状,不包含可执行代码。允许在export default后跟任意表达式,与 TypeScript 在其他环境上下文中处理可执行代码的方式不一致。
因此从 2.6 开始,export default后面任何不是标识符(identifier)或限定名称(qualified name,如ns.name)的内容都会被标记为错误。
正确的声明方式
为具有上述"值形状"的模块编写.d.ts的正确方法如下:
declare module 'foo' { const _default: string; export default _default; }即:先在环境模块内部用const声明一个带类型的值,再将这个标识符作为默认导出。这样既保留了"默认导出是一个 string 类型的值"的类型语义,又避免了在环境上下文中执行表达式。
对编译器生成与手写声明的影响
关键的一点是:TypeScript 编译器自身生成的声明文件(通过--declaration选项输出)早已采用这种"先声明常量再导出"的形式,因此这项改动只影响手工编写的.d.ts文件。如果你在项目中维护第三方库的类型声明,或使用declare module为无类型的 npm 包补充类型,升级后需要检查环境模块中的export default是否出现了表达式。
关于环境模块声明的基本写法,可以参考仓库中的声明文件介绍与模块声明模板,其中对declare module的适用场景和结构有系统说明。
升级注意事项小结
将项目升级到 TypeScript 2.6(或更高版本)时,建议按以下顺序自查:
- 检查编译选项:确认
tsconfig.json或命令行中noUnusedLocals/noUnusedParameters的开启状态。由于默认值为false,未显式开启的项目不会受到第一项改动的影响;开启后则需要注意"只写不读"的参数、局部变量和类私有属性会开始报错,仅自调用的递归函数同样如此。 - 扫描手写声明文件:搜索
.d.ts文件与declare module块中的export default,确认其后是标识符或限定名称,而非字符串拼接、函数调用等任意表达式。 - 关注编译器生成物:由
--declaration生成的声明文件不受影响,无需修改。
完整的破坏性改动列表可在本仓库的Breaking Changes 总览中查看,TypeScript 2.6 同期引入的其他新特性(如--strictFunctionTypes、// @ts-ignore注释等)可参见TypeScript 2.6 发布说明。
- 文档
- 教程
【免费下载链接】TypeScript
TypeScript 使用手册(中文版)翻译。http://www.typescriptlang.org
相关推荐
Mitosis ESLint 规则 only-default-function-and-imports 完全指南:约束组件文件只保留导入、默认导出与类型声明
Mitosis ESLint 规则 only default function and imports 完全指南:约束组件文件只保留导入、默认导出与类型声明 本
前端代码生成开发工具低代码Gel/EdgeDB 链接属性(Link Properties)完全指南:声明、约束、索引与增删改查实战
Gel/EdgeDB 链接属性(Link Properties)完全指南:声明、约束、索引与增删改查实战 本文基于 Gel/EdgeDB 官方文档《Link p
数据库图数据库关系型数据库Penrose Style 形状系统详解:声明语法、画布约束与类型检查机制
Penrose Style 形状系统详解:声明语法、画布约束与类型检查机制 本篇技术指南以 Penrose 官方文档 形状总览 https://link.git
开发工具数据可视化
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考