TypeScript 类型设计——构建健壮代码的基石

给大家推荐一本书:《Effective TypeScript》

在《Effective TypeScript》的第四篇中,类型设计被视为构建健壮、可维护TypeScript代码的核心环节。作者Dan Vanderkam通过62个实践方法中的关键条款,深入剖析了如何通过类型设计提升代码的安全性和可读性。以下是对该篇章核心内容的提炼与扩展。

1. 倾向选择总是代表有效状态的类型

类型设计应优先反映业务逻辑中的有效状态,而非无效或中间状态。例如,在处理用户登录流程时,应定义User类型为包含id、name等字段的完整对象,而非仅包含email的临时状态。
实践建议:

  • 使用never类型或联合类型(如User | null)明确表示无效状态。
  • 避免在类型中暴露内部实现细节(如_isLoading等私有字段)。

2. 宽进严出:输入宽容,输出严格

函数设计应遵循"宽进严出"原则,即接受尽可能宽泛的输入类型,但返回严格限定的输出类型。

例如,一个处理用户输入的函数应接受string | number类型,但返回经过验证的string类型。

实践建议:

  • 使用泛型或条件类型动态调整输入/输出类型。
  • 通过运行时校验(如zod库)确保输入符合预期。

3. 避免在文档中重复类型信息

类型声明本身即文档,应避免在注释中重复类型信息。例如,以下代码是冗余的:

js 复制代码
/**
 * @param {string} name - 用户名称
 */
function greet(name: string) { ... }

实践建议:

  • 使用TSDoc注释描述类型无法表达的业务逻辑(如参数约束)。
  • 对复杂类型,可通过类型别名或接口提升可读性。

4. 将空值推到类型边界

空值(null/undefined)应作为类型的边界条件处理,而非混入核心逻辑。例如,一个返回用户信息的函数应明确返回User | null,而非在内部处理空值。

实践建议:

  • 使用可选链操作符(?.)和空值合并操作符(??)简化空值处理。
  • 对可能为空的字段,使用Partial<T>或自定义工具类型(如Nullable<T>)。

5. 优选接口的联合,而非联合的接口

当需要表示多个可能的状态时,优先使用接口的联合(如type State = Loading | Success | Error),而非联合的接口(如interface LoadingState { ... })。

实践建议:

  • 使用discriminated union(可辨识联合)通过共同字段(如type)区分状态。
  • 对复杂状态,可结合type和interface实现更灵活的设计。

6. 选择更精确的字符串类型的替代类型

避免直接使用string类型,应优先选择更精确的替代类型。例如:

js 复制代码
type Email = string; // 不推荐

type Email = `${string}@${string}.${string}`; // 更精确(需TypeScript 4.1+)

实践建议:

  • 使用字符串字面量类型(如type Status = 'active' | 'inactive')。
  • 对复杂模式,可结合正则表达式或第三方库(如io-ts)进行验证。

7. 从API和规范生成类型

类型应直接从API文档或规范生成,而非手动编写。例如,通过openapi-typescript等工具从OpenAPI规范生成类型。

实践建议:

  • 使用d.ts文件或@types包管理第三方库的类型。
  • 对自定义API,编写类型生成脚本(如基于JSON Schema)。

8. 使用问题域语言命名类型

类型名称应直接反映业务领域概念,而非技术实现。例如,将UserList改为TeamMembers,将DataStore改为CustomerDatabase。

实践建议:

  • 避免使用通用名称(如Manager、Handler),除非它们是领域术语。
  • 通过团队讨论统一术语,避免命名歧义。

9. 考虑加"烙印"实现名义类型

当需要区分本质相同但语义不同的类型时,可通过"烙印"(Brand)模式实现名义类型。例如:

js 复制代码
type UserId = string & { __brand: 'UserId' };
function createUserId(id: string): UserId {
 return id as UserId;
}

实践建议:

  • 使用zod或io-ts等库内置的名义类型支持。
  • 对复杂场景,可结合TypeScript的nominal typing技巧(如私有字段)。

总结

类型设计是TypeScript开发的核心技能。通过遵循"有效状态优先""宽进严出""避免重复类型信息"等原则,开发者可以构建出更安全、更易维护的代码。同时,结合工具链(如类型生成、运行时校验)和领域驱动设计(DDD)思想,可以进一步提升类型设计的表达力和实用性。

《Effective TypeScript》的第四篇不仅提供了具体的实践方法,更传递了一种以类型为中心的编程思维。掌握这些技巧,将帮助开发者从"使用TypeScript"跃升至"精通TypeScript"。

相关推荐
Setsuna_F_Seiei7 小时前
前端转型 Agent 开发 06 之 Agent Memory 记忆系统(让 Agent 更智能,更懂你)
前端·agent·ai编程
zhangzeyuaaa8 小时前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
默_笙8 小时前
🌊 向量库和 ES 都查不出"关系",我只好给奶茶建了一张人脉网
前端·javascript
优选资讯8 小时前
标签打印软件怎么对接 Excel 数据
前端·excel
一个风轻云淡9 小时前
GCC 和 GDB命令简单解读
java·linux·前端
u01110267510 小时前
图片处理接口如何统一错误信息 前端提示与后端错误码设计
前端·状态模式
福兮说10 小时前
浏览器里把 iPhone 的 HEIC 转成 JPG:Chrome 解不开、转完大了七成、拍摄时间全丢,六个坑实测
前端·javascript·图像处理·chrome·ios·iphone·heic
libokaifa10 小时前
把语音模型塞进手机:sherpa-onnx 在 Android 上的 ASR / TTS
前端
deli00711 小时前
DLA 枝晶生长实测:5000 个粒子集体停摆之后,分形维数收敛到 1.70
前端
IT_陈寒11 小时前
Redis卡顿的锅,这次真不是大key的错
前端·人工智能·后端