feat(typescript-type-review): add W6 — nullable container redundancy (Alternative typeclass)

This commit is contained in:
星月 2026-04-25 09:52:37 +08:00
parent 72af2ab3a1
commit 50ac98efba

View File

@ -171,7 +171,50 @@ type SpawnError =
| { kind: "spawn_failed"; message: string }; | { kind: "spawn_failed"; message: string };
``` ```
#### W6: 宽泛类型参数 #### W6: Nullable 容器冗余 — Alternative 类型的 `| null`
当一个类型自身就有"空"表示(Haskell 中称为 Alternative typeclass 的 `empty`),再包一层 `| null` 就引入了两种"没有"的语义,调用方必须同时处理两个分支,且很难分清它们的区别。
常见的 Alternative 类型:`T[]`(empty = `[]`)、`Record<string, T>`(empty = `{}`)、`Map<K,V>``Set<T>``string`(empty = `""`)。
**核心问题:`[]``null` 语义是否不同?如果相同,去掉 `| null`。**
```typescript
// ❌ Bad — [] 和 null 都表示"没有标签",调用方要 if (tags === null || tags.length === 0)
type Post = {
tags: string[] | null;
};
// ✅ Good — 空数组就是"没有"
type Post = {
tags: string[]; // empty array = no tags
};
```
```typescript
// ❌ Bad — {} 和 null 都表示"没有 headers"
type Request = {
headers: Record<string, string> | null;
};
// ✅ Good
type Request = {
headers: Record<string, string>; // empty object = no headers
};
```
**保留 `| null` 的合理场景**:当 `null` 和 empty 确实有不同业务语义时。例如:
```typescript
// ✅ 这里 null = "从未加载过",[] = "加载了但结果为空"
type SearchState = {
results: SearchResult[] | null; // null = not yet searched
};
```
**审查方法**:每次看到 `T[] | null``Record<K,V> | null``Set<T> | null``Map<K,V> | null``string | null` 时,问一句:**empty 和 null 的语义是否不同?** 如果答不上来,去掉 `| null`
#### W7: 宽泛类型参数
`Record<string, unknown>``object` 通常意味着缺少具体类型定义。 `Record<string, unknown>``object` 通常意味着缺少具体类型定义。
```typescript ```typescript