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

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

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

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

类型设计应优先反映业务逻辑中的有效状态,而非无效或中间状态。例如,在处理用户登录流程时,应定义User类型为包含idname等字段的完整对象,而非仅包含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)区分状态。
  • 对复杂状态,可结合typeinterface实现更灵活的设计。

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

实践建议

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

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

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

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

实践建议

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

总结

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

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

相关推荐
甲维斯3 分钟前
0代码,0建模,3句话开发一个3D游戏!
前端·游戏·游戏开发
IT_陈寒15 分钟前
Vue的响应式更新把我坑惨了,原来问题出在这
前端·人工智能·后端
tingke20 分钟前
别再堆 AGENTS.md 了:前端团队的 Agent 上下文分层落地指南
前端
李明卫杭州1 小时前
React 复合事件系统对比解析
前端·javascript·react.js
葡萄城技术团队1 小时前
SpreadJS V19.2 新特性揭秘:FILTERXML 函数
前端
袋鼠云数栈UED团队2 小时前
AI Coding 方法论分析:同一个需求 SpecKit 、 Superpowers、 MattpocockSkills 不同设计
前端·aigc·ai编程
掰头战士2 小时前
Prompt Cache,如果agent全靠LLM方做隐式缓存实在不够用。
前端·llm·agent
LEE2 小时前
前端转型全栈 01:数据建模,前端最大的盲区
前端·javascript·后端
货拉拉技术2 小时前
个人提效,攒不成组织提效:货拉拉 AI Coding 落地实践
前端
雪芽蓝域zzs3 小时前
第四十七节:驾驶舱大屏 ECharts 图表集成
前端·javascript·vue.js