关于 imprecise-over-inaccurate
这项 TypeScript 技能主张优先保证类型准确,而非一味追求精确,帮助开发者避免拒绝有效代码,并判断何时应采用运行时验证。
主要功能
- 01优先保证准确而非狭窄
- 02识别误报式类型错误
- 03复杂领域采用宽泛类型
- 04逐步细化类型
- 05运行时验证不确定值
适用场景
- 01精确类型拒绝有效输入
- 02调试复杂泛型类型
- 03为 CSS 或外部数据建模
- 04设计灵活的 JSON Schema 类型
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill imprecise-over-inaccurate安装后在 Claude Code 中输入/imprecise-over-inaccurate确认已加载。
详细介绍
与其使用不准确的精确类型,不如使用不精确的类型
概述
正确的不精确类型,胜过错误的精确类型。
让类型更加精确时,你可能会使其变得不准确。不准确的类型会导致误报(拒绝有效代码)或漏报(接受无效代码)。这两者都会削弱人们对类型系统的信任。
何时使用此技巧
- 让类型“更加精确”
- 遇到意外的类型错误
- 类型拒绝有效的用例
- 调试复杂的泛型类型
铁律
Types should never reject valid code.
When in doubt, be less precise.
请记住:
- 误报(拒绝有效代码)会削弱信任
- 漏报(接受无效代码)会导致错误
- 不精确但正确 > 精确但错误
- 复杂类型更难调试
检测:过度精确的类型
// Attempt to precisely type CSS colors
type CSSColor =
| 'red' | 'blue' | 'green' | 'yellow' // named colors
| `#${string}` // hex colors
| `rgb(${number}, ${number}, ${number})`; // rgb
function setColor(color: CSSColor) { /* ... */ }
// But this valid CSS is rejected:
setColor('rgb(255,255,255)'); // Error: no spaces around commas!
setColor('rgba(0, 0, 0, 0.5)'); // Error: didn't account for rgba!
这个精确类型并不准确——它会拒绝有效的 CSS。
更好的做法:不精确但准确
// Less precise, but accepts all valid CSS colors
function setColor(color: string) { /* ... */ }
setColor('rgb(255, 255, 255)'); // OK
setColor('rgba(0, 0, 0, 0.5)'); // OK
setColor('invalid'); // OK (imprecise)
string 类型不精确(会接受无效颜色),但很准确(绝不会拒绝有效颜色)。
折中方案:部分精确
// Provide known values, allow escape hatch
type CSSColor =
| 'red' | 'blue' | 'green' | 'yellow'
| (string & {}); // Allows any string with autocomplete for known values
setColor('red'); // Autocomplete works
setColor('custom'); // Still allowed
真实示例:JSON Schema
// Trying to precisely type JSON Schema
interface JSONSchema {
type: 'string' | 'number' | 'object' | 'array' | 'boolean' | 'null';
properties?: Record<string, JSONSchema>;
items?: JSONSchema;
// ... 50 more properties
}
// But real JSON Schema is more flexible:
const schema: JSONSchema = {
type: ['string', 'null'], // Error: type can be array!
// ...
};
这个“精确”类型并不准确。更简单的类型可能更好:
interface JSONSchema {
type?: string | string[];
[key: string]: unknown; // Allow any other properties
}
精确性何时有益
在以下情况下,精确性值得追求:
- 你能控制所有输入(内部类型)
- 领域定义明确(有限且已知的值)
- 错误能尽早发现(开发阶段)
- 类型足够简单,显然是正确的
// Good use of precision: finite, known values
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
// Good use of precision: simple domain
type PositiveInteger = number; // (Could use branded type)
何时降低精确性
在以下情况下,应降低精确性:
- 涉及外部数据(API、用户输入)
- 领域复杂(CSS、HTML、正则表达式)
- 类型拒绝有效的用例
- 类型复杂且难以验证
// Over-precise: might reject valid CSS
type CSSLength = `${number}px` | `${number}em` | `${number}rem`;
// Better: trust the developer
type CSSLength = string;
// Or: document the expected format
/** CSS length value, e.g., "10px", "2em" */
type CSSLength = string;
误报与漏报
| 问题 | 影响 | 解决方案 |
|---|---|---|
| 误报 | 拒绝有效代码 | 降低类型的精确性 |
| 漏报 | 接受无效代码 | 在运行时进行验证 |
误报更糟,因为它们会:
- 阻碍正当工作
- 迫使人们使用变通办法(
as any) - 削弱人们对 TypeScript 的信任
渐进式细化
从不精确开始,按需增加精确性:
// V1: Start broad
interface Config {
[key: string]: unknown;
}
// V2: Add known properties
interface Config {
port?: number;
host?: string;
[key: string]: unknown; // Still allow extras
}
// V3: Remove index signature if confident
interface Config {
port: number;
host: string;
}
抵制压力的原则
1. “越精确越好”
压力:“类型应该尽可能具体”
**回应:**前提是它们也准确。精确但错误 = 更糟。
**行动:**检查:这种精确性会拒绝有效代码吗?
2. “我们应该捕获所有错误”
压力:“类型应该阻止所有无效值”
**回应:**你无法对所有内容进行类型检查。运行时验证可以派上用场。
**行动:**准确描述你能描述的类型,其余部分在运行时验证。
危险信号——停止并重新考虑
- 含有许多联合类型或交叉类型的复杂类型
- 明明知道代码有效,却出现类型错误
- 经常使用
as any绕过类型 - 难以解释的类型
常见的自我辩解(全部不成立)
| 借口 | 事实 |
|---|---|
| “这个类型从技术上来说是正确的” | 如果它拒绝有效代码,那它就不正确 |
| “用户应该写出更好的代码” | 类型应该符合现实,而非理想 |
| “我们可以添加例外” | 需要例外就说明类型有问题 |
快速参考
// DON'T: Over-precise type that rejects valid code
type Color = 'red' | 'blue' | `#${string}`;
setColor('rgb(0,0,0)'); // Error - but it's valid!
// DO: Imprecise type that accepts all valid code
type Color = string;
setColor('rgb(0,0,0)'); // OK
// BETTER: Partial precision with escape hatch
type Color = 'red' | 'blue' | (string & {});
setColor('red'); // Autocomplete works
setColor('rgb(0,0,0)'); // Still allowed
归根结底
准确性胜过精确性。
会接受某些无效值的类型,胜过会拒绝有效值的类型。让类型更加精确时,请确认它们仍然准确。如果精确性导致误报,就降低精确性。
参考资料
基于 Dan Vanderkam 的《Effective TypeScript》第 40 条:与其使用不准确的精确类型,不如使用不精确的类型。
评论
支持 Markdown