mcpField
P
prefer-type-annotations

prefer-type-annotations

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

关于 prefer-type-annotations

prefer-type-annotations 是指导 TypeScript 开发者优先使用类型注解而非类型断言的技能。它强调注解会检查值是否符合类型,并提供识别断言风险及少数适用情形的规则。

主要功能

  1. 01优先用注解验证值的类型
  2. 02识别并重新检查 as 断言
  3. 03为函数返回值添加类型注解
  4. 04了解 DOM 等断言适用情形
  5. 05通过运行时检查缩窄类型

适用场景

  1. 01声明变量或对象字面量类型
  2. 02定义函数返回类型
  3. 03排查不兼容类型错误
  4. 04评估是否应保留类型断言

安装

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

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

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

详细介绍

优先使用类型注解,而非类型断言

概述

类型注解(: Type)会验证值是否符合类型。类型断言(as Type)则是告诉 TypeScript:相信你。

注解会检查你的代码。断言会绕过检查。可以选择时,优先使用注解。

何时使用此技能

  • 为变量赋值
  • 定义函数返回类型
  • 使用对象字面量
  • 想用 as Type 修复错误时
  • 遇到“不可赋值”错误时

铁律

NEVER use type assertions when type annotations would work.

没有例外:

  • 不能因为“这显然没问题”
  • 不能因为“我比 TypeScript 更清楚”
  • 不能因为“断言更简单”

识别:“as”异味

看到 as SomeType?问问自己:“能否改用类型注解?”

// ❌ VIOLATION: Type assertion bypasses checking
const alice = {} as Person;  // No error! But alice has no properties

// ✅ CORRECT: Type annotation verifies the value
const alice: Person = {};
// ~~~~~ Property 'name' is missing in type '{}' but required in type 'Person'

关键区别

类型注解(安全)
interface Person { name: string }

// TypeScript CHECKS that the value matches the type
const bob: Person = { name: 'Bob' };  // OK
const bad: Person = {};                // Error: missing 'name'
const extra: Person = { 
  name: 'Carol', 
  age: 30           // Error: 'age' does not exist on type 'Person'
};
类型断言(不安全)
// TypeScript TRUSTS you that the value matches the type
const bob = {} as Person;       // No error, but wrong!
const bad = { foo: 1 } as Person;  // No error, but wrong!

箭头函数:为返回值添加注解

// ❌ VIOLATION: Assertion hides errors
const people = ['alice', 'bob'].map(name => ({name} as Person));

// Even worse: completely wrong values slip through
const people = ['alice', 'bob'].map(name => ({} as Person));  // No error!

// ✅ CORRECT: Annotate the return type
const people = ['alice', 'bob'].map((name): Person => ({name}));  // OK
const people = ['alice', 'bob'].map((name): Person => ({}));
//                                                     ~~ Error: missing 'name'

何时适合使用断言

只有在你确实比 TypeScript 知道得更多时,类型断言才有意义:

1. DOM 元素
// You know #myButton exists and is a button
const button = document.querySelector('#myButton') as HTMLButtonElement;

// Better: include a comment explaining why
const button = document.querySelector('#myButton') as HTMLButtonElement;
// This button is created in index.html and always exists
2. 运行时检查之后
const el = document.getElementById('foo');
if (el) {
  el.innerHTML = 'Hello';  // TypeScript knows el is not null
}

// Or with non-null assertion (use sparingly!)
const el = document.getElementById('foo')!;

抵御压力的守则

1. “这个错误不对”

压力:“TypeScript 报错了,但我的代码是正确的”

**回应:**TypeScript 通常是对的。仔细阅读错误信息。

行动:

  1. 检查类型定义是否符合你的意图
  2. 检查值是否确实符合该类型
  3. 只有在能解释 TypeScript 为何出错时,才使用断言
2. “断言更简单”

压力:“到处添加注解太冗长了”

**回应:**注解能捕获错误,断言会掩盖错误。安全 > 简洁。

**行动:**添加注解。未来的你会感谢现在的你。

3. “我知道运行时的类型”

压力:“我在运行时检查过类型,所以断言是安全的”

**回应:**如果你在运行时检查过,TypeScript 应该能够缩窄类型范围。

**行动:**使用类型守卫或条件语句来缩窄类型,而不是使用断言。

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

  • 代码中任何位置出现 as any
  • 创建对象后立刻使用 as Type
  • 连续使用多个断言(x as A as B)
  • 使用断言修复“不可赋值”错误
  • 没有充分理由就使用 !(非空断言)

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

借口事实
“我知道它是 Person”那就用注解证明,而不是用断言。
“TypeScript 错了”TypeScript 发现了真实的不一致。查明原因。
“只是一个断言”断言会蔓延。一个会带来更多断言。
“类型限制太严格了”严格的类型能捕获错误。接受它们。

快速参考

场景使用注解使用断言
变量声明:Type从不
函数参数:Type从不
函数返回值:Type很少
对象字面量:Type从不
你确定类型的 DOM 元素-as HTMLElement,并添加注释
空值检查之后-确定时使用 !
来自 API 的未知值-使用 unknown 验证之后

非空断言(!)

// Tells TypeScript: "Trust me, this isn't null"
const el = document.getElementById('foo')!;

// Prefer a runtime check:
const el = document.getElementById('foo');
if (!el) throw new Error('No element #foo');
// Now TypeScript knows el is not null

总之

类型注解是一份契约。类型断言则是你对 TypeScript 撒的谎。

使用注解来声明你的意图。只有当你掌握了 TypeScript 无法获得的信息时,才使用断言。务必添加注释,说明断言为何有效。

参考

基于 Dan Vanderkam 的《Effective TypeScript》第 9 条:优先使用类型注解,而非类型断言。

评论

支持 Markdown