mcpField
I
imprecise-over-inaccurate

imprecise-over-inaccurate

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

关于 imprecise-over-inaccurate

这项 TypeScript 技能主张优先保证类型准确,而非一味追求精确,帮助开发者避免拒绝有效代码,并判断何时应采用运行时验证。

主要功能

  1. 01优先保证准确而非狭窄
  2. 02识别误报式类型错误
  3. 03复杂领域采用宽泛类型
  4. 04逐步细化类型
  5. 05运行时验证不确定值

适用场景

  1. 01精确类型拒绝有效输入
  2. 02调试复杂泛型类型
  3. 03为 CSS 或外部数据建模
  4. 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
}

精确性何时有益

在以下情况下,精确性值得追求:

  1. 你能控制所有输入(内部类型)
  2. 领域定义明确(有限且已知的值)
  3. 错误能尽早发现(开发阶段)
  4. 类型足够简单,显然是正确的
// 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)

何时降低精确性

在以下情况下,应降低精确性:

  1. 涉及外部数据(API、用户输入)
  2. 领域复杂(CSS、HTML、正则表达式)
  3. 类型拒绝有效的用例
  4. 类型复杂且难以验证
// 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