mcpField
N
no-null-in-aliases

no-null-in-aliases

github.com/marius-townhouse开发644 次安装38 周前更新
代码仓库

关于 no-null-in-aliases

这项 TypeScript 技能建议顶层类型别名不包含 null 或 undefined,而在使用处明确标注可空性。它帮助开发者提升类型可读性,并在重构时发现空值相关错误。

主要功能

  1. 01顶层类型别名不包含空值
  2. 02在使用处写明 Type | null
  3. 03必要时明确命名可空别名
  4. 04允许对象属性可空

适用场景

  1. 01定义清晰的领域类型别名
  2. 02明确显示函数参数的可空性
  3. 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