关于 no-null-in-aliases
这项 TypeScript 技能建议顶层类型别名不包含 null 或 undefined,而在使用处明确标注可空性。它帮助开发者提升类型可读性,并在重构时发现空值相关错误。
主要功能
- 01顶层类型别名不包含空值
- 02在使用处写明 Type | null
- 03必要时明确命名可空别名
- 04允许对象属性可空
适用场景
- 01定义清晰的领域类型别名
- 02明确显示函数参数的可空性
- 03重构时更安全地检查空值
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill no-null-in-aliases安装后在 Claude Code 中输入/no-null-in-aliases确认已加载。
详细介绍
避免在类型别名中包含 null 或 undefined
概述
类型别名应该表示某种东西,而不是“某种东西或什么都没有”。
看到 User 时,你应该想到一个用户,而不是一个可能存在的用户。应在使用位置显式包含 null,而不是将其隐藏在类型别名中。
何时使用此技能
- 定义类型别名
- 为包含 null 的类型命名
- 理解令人困惑的可空性
- 设计函数参数
铁律
Type aliases should represent valid values.
Use Type | null explicitly, not NullableType.
请记住:
User应始终表示一个用户User | null明确说明了可空性- 隐藏的可空性会让读者困惑
- 对象中的属性可以是可选的或可空的
检测:隐藏的可空性
// Is user nullable? Can't tell from usage
function getComments(comments: Comment[], user: User) {
return comments.filter(c => c.userId === user?.id);
}
如果类型定义如下:
type User = { id: string; name: string; } | null;
那么就需要使用可选链 ?.。但读者必须查看类型定义才能知道这一点。
更好的做法:显式声明可空性
type User = { id: string; name: string; };
// Now nullability is visible at the usage site
function getComments(comments: Comment[], user: User | null) {
return comments.filter(c => c.userId === user?.id);
}
或者,如果用户是必需的:
function getComments(comments: Comment[], user: User) {
return comments.filter(c => c.userId === user.id);
}
如果必须包含 null
使用一个明确的名称:
// Bad: hidden null
type User = { id: string } | null;
// Better: explicit in name
type NullableUser = { id: string } | null;
// Best: no alias, explicit at usage
type User = { id: string };
function fn(user: User | null) { ... }
嵌套的可空性没问题
此规则适用于类型别名的顶层。对象内的可空属性没有问题:
// OK: nullable property in object
interface BirthdayMap {
[name: string]: Date | undefined;
}
// BAD: nullable at top level
type BirthdayMap = {
[name: string]: Date | undefined;
} | null;
可选属性
可选属性与可空属性类似:
interface User {
id: string;
name: string;
email?: string; // OK: optional property
}
但请参考第 33 条和第 37 条,了解何时适合使用可选属性。
为什么这很重要
代码可读性
// What does this mean?
function processUser(user: User) { ... }
// If User includes null, you'd expect:
function processUser(user: User) {
if (!user) return; // But why? Isn't user required?
}
// Explicit is clearer:
function processUser(user: User | null) {
if (!user) return; // Ah, it might be null!
}
重构安全性
// If User includes null, this might crash:
function processUser(user: User) {
console.log(user.name); // Runtime error if null
}
// With explicit typing, TypeScript catches it:
function processUser(user: User | null) {
console.log(user.name); // Error: 'user' is possibly 'null'
}
抗压原则
1. “这样更简洁”
压力:“NullableUser 比 User | null 短。”
**回应:**在使用位置明确表达,多几个字符也值得。
**行动:**在使用位置使用 Type | null。
2. “它始终可能为空”
压力:“来自 API 的用户始终可能为空。”
**回应:**这是 API 的细节,不是用户本身的属性。
**行动:**在 API 边界处理可空性,而不是将其放进类型中。
危险信号——停下来重新考虑
- 类型别名以
| null或| undefined结尾 - 类型名称为
NullableX或MaybeX - 对你认为必需的参数使用可选链
- 不确定某个类型是否包含 null
常见的自我辩解(都不成立)
| 借口 | 事实 |
|---|---|
| “这说明 null 是可能的” | 它反而隐藏了这一点;显式写出 | null 才能说明这一点 |
| “这样可以减少重复” | 清晰度 > 简洁性 |
| “API 返回的数据就是这样” | 在边界处进行转换 |
快速参考
// DON'T: null in type alias
type User = { id: string } | null;
// DON'T: Maybe/Nullable prefixes
type MaybeUser = User | null;
// DO: Clean type alias
type User = { id: string };
// DO: Explicit nullability at usage
function process(user: User | null) { ... }
// OK: Nullable properties inside objects
interface Config {
timeout?: number; // optional property
data: Data | null; // nullable property
}
总结
类型名称应该表示事物本身,而不是可能存在的事物。
看到 User 时,你应该期待一个用户。在可空性重要的使用位置显式包含 | null。这样能让代码更易读,并帮助 TypeScript 捕获与 null 相关的错误。
参考资料
改编自 Dan Vanderkam 的《Effective TypeScript》第 32 条:避免在类型别名中包含 null 或 undefined。
评论
支持 Markdown