TypeScript 最烦人,也最简单的 error,即:optional 声明方式不兼容。
相信所有 TypeScript 开发者都遇到下面的错误日志吧?😂
Type 'number | null' is not assignable to type 'number | undefined'.
Type 'null' is not assignable to type 'number | undefined'.ts(2322)
虽然这个错误的解决方法很简单,但是时不时就被它烦一下,也是服气了。忍不住想吐槽几句。
TypeScript optional 声明方式不兼容造成了生态问题,不是简单的语法问题。
TJ 2024-01-11
如果只是简单的语法问题,我又何必吐槽呢?
如果只是简单的程序,没有第三方依赖,不涉及工具链,那就做好自己就行了。只要自己和团队做得好,TypeScript optional 声明方式不兼容的问题是可以避免的。
但是对于复杂的程序,有大量第三方依赖,并使用各种工具链辅助开发,那么这就是生态问题。只靠自己和团队避坑是没有用的,因为生态里的总有人会挖好坑等你。
为什么说是生态问题呢?
先解释一下我现在参加的项目的技术栈:
- protobuf 定义 gRPC request response,并通过 codegen 生成 gRPC server 和 client 的 TypeScript 类型定义
- prisma 定义 database schema,并通过 prisma generate 生成 TypeScript 类型定义
- class-validator 定义 validation schema
- TypeScript 编写业务代码和辅助数据
针对最开始分享的 TypeScript error 我分享一下我的 protobuf prisma class-validator TypeScript 分别是怎么写的,如果你是 TypeScript developer,相信你一定能看懂。
1. protobuf and generated code
protobuf code
// .proto
message ChargingProfile {
// ...
optional int32 duration = 2;
// ...
}
protobuf generated code
// dist/buf/check_in_history_pb.ts
export class ChargingProfile extends Message<ChargingProfile> {
// ...
/**
* @generated from field: optional int32 duration = 2;
*/
duration?: number;
// ...
2. prisma and generated code
prisma schema
// schema.prisma
model ChargingProfile {
// ...
duration Int?
// ...
}
prisma schema generated code
// node_module/.prisma/client/index.d.ts
export type ChargingProfile = {
// ...
duration: number | null
// ...
}
小结
注意看,protobuf generated code 和 prisma schema generated code
// protobuf generated code
+ duration?: number;
//prisma schema generated code
+ duration: number | null
最开始分享的 TypeScript error 就是因为这个原因造成的,我在 x.com 上分享了这段代码,很多朋友分享了他们的避免方法。但是 generated code
完全不是我编写的,而是 TypeScript 生态中工具链生成的,即使我的定义是相同的,不同工具链生成的代码还是造成了 TypeScripr error,我真不知道我要如何避免这个错误。🤷
BTW 这个问题的解决方法非常简单,我并不是不能解决。 只是它经常出来烦我一下,让我忍不住想吐槽。
TJ 2024-01-11
3. class-validator
export class ChargingProfileDto {
// ...
@IsOptional()
@IsInt()
duration?: number;
// ...
}
其实 class-validator 实现的代码和本文没什么关系,不过这段代码是真实存在于我们的代码库中的,我就顺便分享一下。
并且,我非常不喜欢 class-validator,原因有以下几点:
- class-validator 和 TypeScript 在某些方面是重复的,例如
@IsOptional()
和?
- class-validator 只能校验 class instance,如果想校验 JSON 还需要额外依赖(class-transformer)
- class-validator 文档不全,例如:我一直没有找到
@IsNumber(options: IsNumberOptions)
中 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
}
大家参加的项目中用哪一种呢?就个人而言,你倾向哪一种?
欢迎大家和我一起讨论:
- x.com: https://x.com/ThaddeusJiang/status/1745085873807347771
- telegram: https://t.me/talktalk_developer/539
- github: https://github.com/ThaddeusJiang/thaddeusjiang.github.io/discussions/63
other refs