Effective TypeScript
避免使用 JavaScript 对象包装类型
避免使用 JavaScript 的对象包装类型(如 `String`、`Number`),应始终优先使用基本类型。
基本类型与对象包装类型
JavaScript 中有七种基本类型值,包括 string、number、boolean、symbol 和 bigint 等[^1]。这些基本类型与对象(Object)的关键区别在于它们是不可变的,且不拥有方法[^2]。
然而,当你在一个基本类型值(例如字符串字面量 "hello")上调用方法时,JavaScript 会执行隐式转换,暂时将其转换为对应的对象包装类型(例如 String)以允许方法调用[^2]。一旦操作完成,该临时对象就会被丢弃[^3]。这种机制导致了一个令人困惑的运行时现象:如果你尝试给一个基本类型赋值属性,该属性会立即消失[^4]:
let x = "hello"; // x 是基本类型 string
x.language = 'English'; // 隐式转换为 String 对象,属性被设置
console.log(x.language); // x 再次隐式转换为 String 对象,但这次是新的临时对象
// 输出: undefined // 原始的 String 对象已被丢弃TypeScript 中的类型区分与问题
TypeScript 明确区分了基本类型(小写)和它们的对象包装类型(大写):
string和Stringnumber和Numberboolean和Booleansymbol和Symbolbigint和BigInt[^5]
Item 10 强烈建议开发者避免使用这些对象包装类型,始终坚持使用小写的基本类型[^6]。
当开发者不小心使用了对象包装类型时,最主要的问题出现在赋值操作和函数调用中。尽管基本类型可以赋值给它们的包装类型(因为结构兼容),但包装类型不能赋值给基本类型[^7]:
function isGreeting(phrase: string) { /* ... */ }
const wrapperString = new String("hello");
const primitiveString = "world";
// 1. 允许:基本类型可以赋值给包装类型
const s: String = primitiveString; // OK
// 2. 核心问题:包装类型不能赋值给基本类型
isGreeting(wrapperString);
// Argument of type 'String' is not assignable to parameter of type 'string'.
// 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible.如果代码中不慎使用大写字母进行了类型注解,尽管运行时值仍为基本类型(因为 TypeScript 类型会被擦除,无法影响运行时行为[^8]),但这种标注是具有误导性和冗余的[^9]:
const s: String = "primitive"; // 运行时 s 仍然是基本类型 string最佳实践总结
- 坚持使用小写的基本类型:始终使用
string、number、boolean、symbol和bigint,避免使用大写的对象包装类型,以确保类型系统能够正确检查函数的参数和返回值[^6]。 - 避免实例化:通常情况下,没有理由直接实例化对象包装类型(即使用
new String(...)或new Number(...))。 - Symbol 和 BigInt 的例外:在调用
Symbol()或BigInt()时,即使不使用new关键字,它们也会创建基本类型的值(symbol或bigint),因此这些构造函数的使用是允许的[^10]。
在 GitHub 上编辑
上次更新于