2024-01-11 TypeScript 最烦人,也最简单的 error
内容提要
TypeScript中的optional声明方式不兼容是最烦人也最简单的错误。复杂程序中的第三方依赖和工具链会导致生态问题。分享了protobuf、prisma和class-validator的代码,指出生成的代码会导致难以避免的TypeScript错误。对class-validator不满意,认为它与TypeScript重复且文档不全。最后分享了几种number的定义,欢迎讨论。
延伸解读
工具链生成的类型不一致是错误根源
文章指出,protobuf 生成的代码将 optional 字段表示为 `duration?: number`,而 prisma 生成的代码表示为 `duration: number | null`。两者对“可选”的语义不同,导致 TypeScript 类型不兼容。由于这些代码由工具链自动生成,开发者难以直接修改,因此错误在复杂项目中反复出现。
生态问题难以靠团队规范避免
作者强调,在简单项目中可以通过团队约定避免 optional 声明不一致,但复杂项目依赖大量第三方工具和代码生成器,不同工具对 optional 的建模方式不同,总会引入不兼容的类型。因此这不仅是语法问题,更是生态协作问题,仅靠自身代码规范无法根治。
class-validator 的重复与文档不足
作者对 class-validator 提出两点不满:一是 `@IsOptional()` 与 TypeScript 的 `?` 在功能上重复;二是它只能校验 class 实例,校验 JSON 需额外依赖 class-transformer。此外,文档不全,例如找不到 `@IsNumber` 的 options 配置项说明,增加了使用成本。
Q&A
TypeScript 里 optional 声明不兼容导致的报错具体是什么?
报错信息通常是:Type 'number | null' is not assignable to type 'number | undefined'。这是因为 TypeScript 中 optional 属性(如 `a?: number`)的类型是 `number | undefined`,而其他工具生成的类型可能是 `number | null`,两者不兼容。
为什么说 TypeScript optional 不兼容是生态问题而不是简单的语法问题?
在简单程序中没有第三方依赖时,可以靠团队规范避免。但在复杂程序中,大量第三方依赖和工具链(如 protobuf、prisma)生成的代码类型定义不一致,即使自己代码写对了,也会因为生态中其他工具生成的类型而报错,防不胜防。
protobuf 和 prisma 生成的 TypeScript 类型在 optional 字段上有什么不同?
protobuf 生成的代码中 optional 字段类型为 `duration?: number`(即 `number | undefined`),而 prisma 生成的类型为 `duration: number | null`。这种差异导致在赋值时出现类型不兼容错误。
作者对 class-validator 有哪些不满?
作者不喜欢 class-validator 的原因有:1) 与 TypeScript 功能重复,例如 @IsOptional() 和 ? 作用类似;2) 只能校验 class 实例,校验 JSON 需要额外依赖 class-transformer;3) 文档不全,例如找不到 @IsNumber 的 options 配置项说明。
TypeScript 中定义 number 类型有哪几种常见方式?
文章列举了五种:`type A = { a?: number }`、`type B = { a: number | undefined }`、`type C = { a: number | null }`、`type D = { a: number | null | undefined }`、`type E = { a: number = 0 }`(默认参数写法)。
如何解决 TypeScript optional 不兼容的错误?
文章提到解决方法非常简单,但未具体说明。作者主要吐槽该错误频繁出现且源于生态工具链,并非无法解决。实际中可根据情况使用类型断言、联合类型或调整工具配置来规避。