mcpField
A
avoid-wrapper-types

avoid-wrapper-types

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

关于 avoid-wrapper-types

这项 TypeScript 技能说明应使用 `string`、`number` 等原始类型,而非对象包装类型,帮助开发者避免类型不匹配及运行时问题。

主要功能

  1. 01使用小写原始类型
  2. 02识别包装类型不匹配
  3. 03了解对象身份与真值陷阱
  4. 04通过自动装箱使用原始值方法

适用场景

  1. 01为 TypeScript 原始值标注类型
  2. 02排查包装类型赋值错误
  3. 03检查代码中的意外包装对象

安装

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

$npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill avoid-wrapper-types

安装后在 Claude Code 中输入/avoid-wrapper-types确认已加载。

详细介绍

避免使用对象包装类型(String、Number、Boolean、Symbol、BigInt)

概述

使用小写原始类型,绝不使用大写包装类型。

JavaScript 有原始类型(string、number、boolean)和对象包装类型(String、Number、Boolean)。TypeScript 同时支持这两类类型,但你几乎不应该使用包装类型。

何时使用本指南

  • 为任何原始值指定类型时
  • 遇到有关包装类型不匹配的错误时
  • 想了解为什么 String !== string 时
  • 使用原始类型的方法时

铁律

ALWAYS use lowercase: string, number, boolean, symbol, bigint
NEVER use uppercase: String, Number, Boolean, Symbol, BigInt

请记住:

  • 包装类型是对象,原始类型不是
  • 需要包装类型时,TypeScript 会直接接受原始类型
  • 需要原始类型时,TypeScript 不会接受包装类型
  • 原始类型通过自动装箱来调用方法

检测:包装类型陷阱

// These look similar but behave differently
const primitive: string = "hello";
const wrapper: String = new String("hello");

// Equality fails
primitive === wrapper;  // false (different types)
wrapper === wrapper;    // Wait, this is also comparing to itself...

const s1 = new String("hello");
const s2 = new String("hello");
s1 === s2;  // false! Each wrapper is a unique object

原始类型的方法如何工作

原始类型没有方法,但下面的代码可以正常运行:

'primitive'.charAt(3);  // 'm' - how?

JavaScript 会自动装箱:

  1. 创建临时的 String 包装对象
  2. 在包装对象上调用方法
  3. 丢弃包装对象
  4. 返回结果

这样既能方便地调用方法,又能避免包装类型的缺点。

类型赋值问题

// Primitive assignable to wrapper (OK but don't do this)
const s: String = "primitive";  // Works

// Wrapper NOT assignable to primitive (Error)
function takesString(s: string) { }
takesString(new String("hello"));
//          ~~~~~~~~~~~~~~~~~~~
// Argument of type 'String' is not assignable to parameter of type 'string'.
// 'string' is a primitive, but 'String' is a wrapper object.

所有原始类型

原始类型(使用)包装类型(避免)
stringString
numberNumber
booleanBoolean
symbolSymbol
bigintBigInt

注意:null 和 undefined 没有包装类型。

包装类型的问题

标识问题
const s1 = new String("hello");
const s2 = new String("hello");

s1 == s2;   // false
s1 === s2;  // false
// Each is a distinct object
typeof 带来的困惑
typeof "hello";              // "string"
typeof new String("hello");  // "object"
真值判断陷阱
const falsy = new Boolean(false);
if (falsy) {
  console.log("This runs!"); // Wrapper objects are always truthy
}

例外:运行时方法

你可以使用包装类型的静态方法:

// These are fine - not creating wrapper objects
String.fromCharCode(65);  // "A"
Number.isNaN(x);
BigInt(123);              // Creates primitive, not wrapper

但绝不要将它们用作类型,也不要创建实例。

抵制压力指南

1. “我需要 String 方法”

压力:“我想使用 String 方法,所以我要把类型写成 String”

**回应:**原始类型 string 通过自动装箱也拥有所有相同的方法。

**行动:**使用 string 类型。方法可以直接调用。

2. “TypeScript 推断出了 String(大写)”

压力:“TypeScript 给出了 String,所以这肯定没错”

**回应:**检查你的代码——你可能不小心创建了包装对象。

**行动:**找到并修复源头。使用小写类型。

危险信号——停下来重新考虑

  • 类型注解中出现大写的 String、Number、Boolean
  • 任何位置出现 new String()、new Number()、new Boolean()
  • 有关包装类型与原始类型赋值的错误
  • 预期值为原始类型时,却检查 typeof x === "object"

常见借口(都不成立)

借口事实
“String 有更多方法”不,两者的方法完全相同
“我需要一个对象”你很少需要;原始类型更简单
“这是一回事”不,包装类型会带来标识和真值判断问题

快速参考

// ALWAYS use these
const s: string = "hello";
const n: number = 42;
const b: boolean = true;
const sym: symbol = Symbol();
const big: bigint = 9007199254740991n;

// NEVER use these
const s: String = "hello";   // Wrong
const n: Number = 42;        // Wrong
const b: Boolean = true;     // Wrong

总结

始终使用小写原始类型。

绝没有理由将 String、Number 或 Boolean 用作类型。大写形式会导致赋值错误和标识问题,而且相比小写原始类型毫无优势。

参考

基于 Dan Vanderkam 的《Effective TypeScript》第 10 条:避免使用对象包装类型(String、Number、Boolean、Symbol、BigInt)。

评论

支持 Markdown