mcpField
T
type-coverage

type-coverage

github.com/marius-townhouse测试与质量643 次安装38 周前更新
代码仓库

关于 type-coverage

这项 TypeScript 技能介绍如何衡量具有具体类型而非 any 的符号比例,并持续跟踪变化。它适合迁移 JavaScript 或维护类型安全的团队,可设置覆盖率门槛并在 CI 中发现退化。

主要功能

  1. 01衡量有类型符号的覆盖率
  2. 02定位类型为 any 的符号
  3. 03支持设置最低覆盖率门槛
  4. 04跟踪变化并防止覆盖率退化

适用场景

  1. 01监控 JavaScript 迁移期间的类型安全
  2. 02在代码审查中检查覆盖率
  3. 03覆盖率低于门槛时使 CI 失败
  4. 04安排 any 类型清理工作

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill type-coverage

安装后在 Claude Code 中输入/type-coverage确认已加载。

详细介绍

跟踪类型覆盖率,防止类型安全回退

概述

监控代码中使用真实类型与 any 的比例。

类型覆盖率用于衡量具有真实类型而非 any 的符号所占的百分比。持续跟踪,防止类型安全回退。

何时使用此技能

  • 将 JavaScript 迁移到 TypeScript
  • 长期维护类型质量
  • 在代码审查中检查类型安全
  • 衡量消除 any 的进度

铁律

What gets measured gets managed.
Track type coverage; set coverage goals.

请记住:

  • any 会在代码中悄然扩散
  • 覆盖率可能在不知不觉中下降
  • 明确跟踪可以防止质量滑坡
  • 为团队设定提高覆盖率的目标

了解类型覆盖率

let x: number = 1;        // x is covered (has real type)
let y: any = 2;           // y is NOT covered (has any)
let z = JSON.parse('{}'); // z is NOT covered (implicit any from JSON.parse)

类型覆盖率 =(已覆盖符号数)/(符号总数)

测量覆盖率的工具

type-coverage 包
npm install -g type-coverage
type-coverage --detail

输出:

23384/24058 97.20%

使用 --detail 可以查看哪些符号使用了 any:

src/api.ts:15:7 - data
src/utils.ts:42:3 - result
项目专属配置
// package.json
{
  "scripts": {
    "type-coverage": "type-coverage --at-least 95"
  }
}

如果覆盖率低于阈值,则让 CI 失败。

any 的来源

1. 显式 any
function process(data: any) { ... }  // Developer wrote any
2. 隐式 any(noImplicitAny 关闭时)
function process(data) { ... }  // data is implicitly any
3. 来自库的 any
const data = JSON.parse(str);   // Returns any
const result = $.ajax(url);     // jQuery returns any
4. 传染性 any
function getUser(): any { ... }
const user = getUser();
//    ^? any - spreads from function

const name = user.name;
//    ^? any - continues spreading

改进策略

替换 JSON.parse
// Before: returns any
const data = JSON.parse(str);

// After: validate with zod
const schema = z.object({ name: z.string() });
const data = schema.parse(JSON.parse(str));
//    ^? { name: string }
修复库的类型
// Augment JSON.parse to return unknown
declare global {
  interface JSON {
    parse(text: string): unknown;
  }
}
改用 unknown
// Before
function parse(): any { ... }

// After
function parse(): unknown { ... }
添加类型注解
// Before: inferred as any from library
const result = externalLib.process(data);

// After: explicitly typed
const result: ProcessedData = externalLib.process(data);

长期跟踪

# Record in CI
echo "$(date): $(type-coverage)" >> coverage-history.txt

# Fail if coverage decreased
type-coverage --at-least $(cat .type-coverage-baseline)

设定团队目标

里程碑覆盖率目标
迁移开始50%
阶段 175%
阶段 290%
稳定阶段95%+

随着代码库日渐成熟,逐步提高目标。

防止回退

提交前钩子
# .husky/pre-commit
type-coverage --at-least 95
CI 检查
# .github/workflows/ci.yml
- name: Type Coverage
  run: npx type-coverage --at-least 95
代码审查检查清单
  • 未经说明,不添加新的 any 类型
  • 类型覆盖率没有下降
  • 外部数据经过验证

处理必要的 any

有时 any 无法避免:

// Document why any is needed
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const legacy: any = oldSystem.getData();  // TODO: Type when migrating legacy system

通过注释和工单跟踪这些情况。

实际示例

// Before migration: 60% coverage
// Sources of any:
// - JSON.parse: 15%
// - Legacy API: 12%
// - Untyped dependencies: 8%
// - Explicit any: 5%

// Action plan:
// 1. Add zod validation: +10%
// 2. Type legacy API responses: +10%
// 3. Add @types packages: +8%
// 4. Remove explicit any: +5%

// After: 93% coverage

抵抗压力的方案

1. “我们无法达到 100%”

压力:“有些代码无法添加类型”

**回应:**目标是不断改进,而非追求完美。尽可能跟踪。

**行动:**设定切实可行的目标;记录必要的例外情况。

2. “工作量太大了”

压力:“修复所有 any 类型要花太多时间”

**回应:**逐步改进。先阻止新增 any 类型。

**行动:**逐步收紧标准:禁止新增 any,再逐步修复旧有问题。

危险信号——停止并重新考虑

  • 覆盖率持续下降
  • 未经说明就新增 any 类型
  • any 从函数返回值开始扩散
  • 代码未经测试且大量使用 any

常见的自我安慰(都不成立)

借口事实
“只是一个 any”any 会扩散;一个会变成很多个
“以后再修”不跟踪,就永远不会有以后
“覆盖率已经够高了”随着改进,应设定更高的目标

快速参考

# Measure coverage
npx type-coverage

# With details
npx type-coverage --detail

# Fail if below threshold
npx type-coverage --at-least 95

# In package.json
"scripts": {
  "type-coverage": "type-coverage --at-least 95"
}

归根结底

跟踪类型覆盖率,防止回退。

any 类型会悄然扩散。如果不进行度量,类型安全就会随着时间推移而逐渐削弱。使用工具跟踪覆盖率、设定目标,并在覆盖率下降时让构建失败。

参考资料

改编自 Dan Vanderkam 的《Effective TypeScript》第 49 条:跟踪类型覆盖率,防止类型安全回退。

评论

支持 Markdown