From 50ac98efba090236f30d2bfbbdaad6d2409a47dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=9F=E6=9C=88?= Date: Sat, 25 Apr 2026 09:52:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(typescript-type-review):=20add=20W6=20?= =?UTF-8?q?=E2=80=94=20nullable=20container=20redundancy=20(Alternative=20?= =?UTF-8?q?typeclass)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hermes/typescript-type-review/SKILL.md | 45 +++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/hermes/typescript-type-review/SKILL.md b/hermes/typescript-type-review/SKILL.md index 5e8a4cf..18b49f7 100644 --- a/hermes/typescript-type-review/SKILL.md +++ b/hermes/typescript-type-review/SKILL.md @@ -171,7 +171,50 @@ type SpawnError = | { kind: "spawn_failed"; message: string }; ``` -#### W6: 宽泛类型参数 +#### W6: Nullable 容器冗余 — Alternative 类型的 `| null` + +当一个类型自身就有"空"表示(Haskell 中称为 Alternative typeclass 的 `empty`),再包一层 `| null` 就引入了两种"没有"的语义,调用方必须同时处理两个分支,且很难分清它们的区别。 + +常见的 Alternative 类型:`T[]`(empty = `[]`)、`Record`(empty = `{}`)、`Map`、`Set`、`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 | null; +}; + +// ✅ Good +type Request = { + headers: Record; // empty object = no headers +}; +``` + +**保留 `| null` 的合理场景**:当 `null` 和 empty 确实有不同业务语义时。例如: + +```typescript +// ✅ 这里 null = "从未加载过",[] = "加载了但结果为空" +type SearchState = { + results: SearchResult[] | null; // null = not yet searched +}; +``` + +**审查方法**:每次看到 `T[] | null`、`Record | null`、`Set | null`、`Map | null`、`string | null` 时,问一句:**empty 和 null 的语义是否不同?** 如果答不上来,去掉 `| null`。 + +#### W7: 宽泛类型参数 `Record` 或 `object` 通常意味着缺少具体类型定义。 ```typescript