关于 domain-language-types
一项 TypeScript 开发技能,指导开发者使用问题领域的概念命名类型,让领域专家也能理解类型所代表的含义。
主要功能
- 01使用领域语言命名类型
- 02将结构化名称改为有意义的名称
- 03避免匈牙利命名法前缀和冗余后缀
- 04采用领域专家共用的术语
适用场景
- 01为新功能命名类型
- 02重构含义不清的类型名称
- 03与领域专家一起评审 API
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill domain-language-types安装后在 Claude Code 中输入/domain-language-types确认已加载。
详细介绍
使用问题领域的语言为类型命名
概述
类型名称应该传达含义,而不是结构。
名为 INodeNameValueData 的类型毫无信息量。名为 APIResponse 或 CustomerOrder 的类型则能说明一切。使用业务领域的语言,而不是数据结构的语言。
何时使用这项技能
- 为新类型命名
- 重构含义不清的类型名称
- 与领域专家一起审查代码
- 设计公共 API
铁律
Type names should be meaningful to domain experts.
If a non-programmer can't understand it, rename it.
请记住:
- 业务术语 > 技术术语
- 含义 > 结构
- 名称就是文档
- 领域专家应该能认出这些类型
检测:结构化名称
// Bad: describes structure, not meaning
interface IEntityWithIdAndName {
id: string;
name: string;
}
interface IStringPairList {
items: [string, string][];
}
interface IDataRecord {
data: Record<string, unknown>;
}
IEntityWithIdAndName 到底是什么?用户?产品?类别?
更好的做法:领域名称
// Good: describes what it IS
interface User {
id: string;
name: string;
}
interface TranslationPairs {
items: [sourcePhrase: string, targetPhrase: string][];
}
interface CustomerProfile {
data: Record<string, unknown>;
}
实际示例:电子商务
// Bad: generic/structural names
interface IData {
id: string;
props: Record<string, unknown>;
}
interface IEntity extends IData {
type: string;
}
interface ICollection {
items: IEntity[];
total: number;
}
对比:
// Good: domain language
interface Product {
sku: string;
name: string;
price: Money;
inventory: number;
}
interface Order {
orderNumber: string;
customer: Customer;
items: OrderItem[];
total: Money;
}
interface ProductCatalog {
products: Product[];
totalCount: number;
}
避免以下模式
匈牙利命名法前缀
// Bad: I prefix for interfaces
interface IUser { }
interface IProduct { }
// Good: just the name
interface User { }
interface Product { }
类型后缀
// Bad: redundant suffixes
interface UserType { }
interface ProductInterface { }
interface OrderObject { }
// Good: clean names
interface User { }
interface Product { }
interface Order { }
通用技术术语
// Bad: generic
interface DataModel { }
interface EntityRecord { }
interface ItemContainer { }
// Good: specific
interface Invoice { }
interface ShippingAddress { }
interface ShoppingCart { }
属性名称也一样
// Bad: technical property names
interface User {
dataString: string;
valueNumber: number;
itemsArray: Product[];
}
// Good: domain property names
interface User {
email: string;
age: number;
purchases: Product[];
}
上下文很重要
相同的结构,不同的领域:
// Healthcare domain
interface Patient {
mrn: string; // Medical Record Number
admissionDate: Date;
diagnosis: string[];
}
// Education domain
interface Student {
studentId: string;
enrollmentDate: Date;
courses: string[];
}
结构相似,但名称传达的含义不同。
技术名称也适用的情况
标准技术概念
// OK: widely understood technical terms
interface HttpRequest { }
interface DatabaseConnection { }
interface CacheEntry { }
通用工具
// OK: truly generic utilities
type Nullable<T> = T | null;
type AsyncResult<T> = Promise<T>;
内部实现
// OK for internal implementation details
interface InternalCacheNode<T> { }
interface TreeNodeImpl<T> { }
通用语言
领域驱动设计要求使用开发人员和领域专家共同理解的“通用语言”。
// If domain experts say "fulfillment", use that:
interface OrderFulfillment { }
// Not:
interface OrderProcessingData { }
interface IOrderExecutionEntity { }
抵御压力的方案
1. “这是技术细节”
压力:“用户不会看到这个类型名称”
**回应:**开发人员会看到。未来的你也会看到。让它有明确含义。
**行动:**根据它代表什么来命名,而不是根据它的结构来命名。
2. “我们有命名规范”
压力:“我们的风格指南要求使用 I 前缀”
**回应:**规范应该提升清晰度,而不是妨碍清晰度。
**行动:**质疑那些降低清晰度的规范。IUser 没有任何增益。
危险信号——停下来重新考虑
- 类型名称以 I、T 开头,或以 Type/Interface/Object 结尾
- 描述结构的名称(StringArray、NumberMap)
- 领域专家无法识别的名称
- 多个都可以称为“Data”的类型
常见的自我辩解(都不成立)
| 借口 | 事实 |
|---|---|
| “它和我们的后端一致” | 后端也可能有糟糕的名称 |
| “这只是内部代码” | 内部代码也需要清晰 |
| “这是我们的规范” | 糟糕的规范应该改变 |
快速参考
// DON'T: Structural/technical names
interface IDataEntity { }
interface StringValuePair { }
interface ItemsCollection<T> { }
// DO: Domain language names
interface Customer { }
interface PriceRange { } // [min: Money, max: Money]
interface ProductCatalog { }
// DON'T: Redundant suffixes
interface UserType { }
interface ProductInterface { }
// DO: Clean names
interface User { }
interface Product { }
总结
根据类型的含义命名,而不是根据它的结构命名。
领域专家应该能通过类型名称理解它们代表什么。Invoice 比 IFinancialDataRecord 说明的信息更多。使用问题领域的语言。
参考资料
基于 Dan Vanderkam 的《Effective TypeScript》第 41 条:使用问题领域的语言为类型命名。
评论
支持 Markdown