avoid-wrapper-types
Avoid Object Wrapper Types (String, Number, Boolean, Symbol, BigInt)
Overview
Use lowercase primitive types, never uppercase wrapper types.
JavaScript has primitive types (string, number, boolean) and object wrapper types (String, Number, Boolean). TypeScript has types for both, but you should almost never use the wrapper types.
When to Use This Skill
- Typing any primitive value
- Seeing errors about wrapper type mismatches
- Understanding why
String !== string - Working with methods on primitives
The Iron Rule
ALWAYS use lowercase: string, number, boolean, symbol, bigint
NEVER use uppercase: String, Number, Boolean, Symbol, BigInt
Remember:
- Wrappers are objects, primitives are not
- TypeScript freely accepts primitives where wrappers expected
- TypeScript does NOT accept wrappers where primitives expected
- Methods work on primitives via auto-boxing
Detection: The Wrapper Trap
// 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
How Primitive Methods Work
Primitives don't have methods, but this works:
'primitive'.charAt(3); // 'm' - how?
JavaScript auto-boxes:
- Creates temporary
Stringwrapper - Calls method on wrapper
- Discards wrapper
- Returns result
You get convenience without the wrapper's drawbacks.
The Type Assignment Problem
// 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.
All Primitive Types
| Primitive (USE) | Wrapper (AVOID) |
|---|---|
string |
String |
number |
Number |
boolean |
Boolean |
symbol |
Symbol |
bigint |
BigInt |
Note: null and undefined don't have wrapper types.
Why Wrappers Are Problematic
Identity Issues
const s1 = new String("hello");
const s2 = new String("hello");
s1 == s2; // false
s1 === s2; // false
// Each is a distinct object
typeof Confusion
typeof "hello"; // "string"
typeof new String("hello"); // "object"
Truthiness Gotchas
const falsy = new Boolean(false);
if (falsy) {
console.log("This runs!"); // Wrapper objects are always truthy
}
Exception: Runtime Methods
You can use wrappers for their static methods:
// These are fine - not creating wrapper objects
String.fromCharCode(65); // "A"
Number.isNaN(x);
BigInt(123); // Creates primitive, not wrapper
But never use them as types or create instances.
Pressure Resistance Protocol
1. "I Need String Methods"
Pressure: "I want to use String methods, so I'll type it as String"
Response: Primitive string has all the same methods via auto-boxing.
Action: Use string type. Methods just work.
2. "TypeScript Inferred String (Uppercase)"
Pressure: "TypeScript gave me String, so it must be right"
Response: Check your code - you might be creating a wrapper accidentally.
Action: Find and fix the source. Use lowercase.
Red Flags - STOP and Reconsider
- Uppercase
String,Number,Booleanin type annotations new String(),new Number(),new Boolean()anywhere- Errors about wrapper vs primitive assignment
typeof x === "object"when expecting a primitive
Common Rationalizations (All Invalid)
| Excuse | Reality |
|---|---|
| "String has more methods" | No, both have identical methods |
| "I need an object" | You rarely do; primitives are simpler |
| "It's the same thing" | No, wrappers have identity and truthiness issues |
Quick Reference
// 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
The Bottom Line
Always use lowercase primitive types.
There's never a good reason to use String, Number, or Boolean as types. The uppercase versions cause assignment errors, have identity issues, and provide no benefits over the lowercase primitives.
Reference
Based on "Effective TypeScript" by Dan Vanderkam, Item 10: Avoid Object Wrapper Types (String, Number, Boolean, Symbol, BigInt).
More from marius-townhouse/effective-typescript-skills
narrow-any-scope
Use when any is unavoidable. Use when working with untyped libraries. Use when silencing specific type errors.
35exhaustiveness-checking
Use when handling tagged unions. Use when adding new cases to discriminated unions. Use when switch statements must cover all cases.
13code-gen-independent
Use when confused about types at runtime. Use when trying to use instanceof with interfaces. Use when type errors don't prevent JavaScript output.
12module-by-module-migration
Use when migrating large codebases. Use when converting JavaScript to TypeScript. Use when managing dependencies. Use when planning migration order. Use when teams are adopting TypeScript.
11editor-interrogation
Use when debugging type inference. Use when types behave unexpectedly. Use when learning unfamiliar code.
11precise-string-types
Use when working with string-typed properties. Use when string values have a limited set of options. Use when keyof could provide better type safety.
10