String vs string in TypeScript: which to choose for speed?

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

String vs string: the silent killer of your code's performance

\n\n

Have you ever wondered why TypeScript complains about String in one place but not about string in another? Or maybe you've seen experienced developers wince at the sight of new String("hello")? This question is a real hit on Stack Overflow, garnering thousands of views. And for good reason. The difference between String and string is not just a quirk of the language, but a fundamental distinction that affects the performance, safety, and readability of your code.

\n\n

In this article, we won't just give a dry definition. We'll break down what lies behind each of these types, how they behave at runtime, and why making the right choice can speed up your application by tens of percent. Get ready, it's going to be interesting!

\n\n

Primitives vs Objects: the basics

\n\n

To understand the difference, you need to remember how JavaScript (and therefore TypeScript) works with data. The language has two types of values:

\n\n
    \n
  • Primitives: string, number, boolean, null, undefined, symbol, bigint. They are immutable and stored by value.
  • \n
  • Objects: Object, Array, Function, as well as String (with a capital letter), Number, Boolean. They are mutable and stored by reference.
  • \n
\n\n

When you write let name: string = "Alice", you create a primitive. When you write let name: String = new String("Alice"), you create a wrapper object. And this is where things get interesting.

\n\n

The string type (lowercase)

\n

This is a primitive type. It is used in 99.9% of cases. It is lightweight, fast, and predictable. Comparison of two primitives is done by value:

\n\n
let a: string = "hello";\nlet b: string = "hello";\nconsole.log(a === b); // true
\n\n

TypeScript recommends using this one. It is the de facto standard for declaring string variables, function parameters, and return values.

\n\n

The String type (uppercase)

\n

This is an object type, a wrapper constructor. It has existed since the days of JavaScript for working with string methods. However, its direct use is more of an anti-pattern, unless you have a very specific task. Comparison of two String objects is done by reference:

\n\n
let a: String = new String("hello");\nlet b: String = new String("hello");\nconsole.log(a === b); // false! These are different objects in memory
\n\n

This can lead to hard-to-find bugs. Imagine you are checking if a string value equals "hello", but you get false just because someone used new String().

\n\n

Performance: why does string win?

\n\n

Now let's get to the best part — numbers and optimization. Let's write a simple performance test (you can run it in your environment, for example, in Node.js or in a browser).

\n\n
// Performance test\nconst iterations = 1_000_000;\n\nconsole.time("string (primitive)");\nlet sum = "";\nfor (let i = 0; i < iterations; i++) {\n    sum += "a";\n}\nconsole.timeEnd("string (primitive)");\n\nconsole.time("String (object)");\nlet sumObj = new String("");\nfor (let i = 0; i < iterations; i++) {\n    sumObj += "a"; // Autoboxing and unboxing happens here\n}\nconsole.timeEnd("String (object)");
\n\n

The result on a typical machine will be dramatically different (in favor of the primitive by 2-5 times). Why? Because:

\n
    \n
  • The primitive string is simply a sequence of characters in memory. Operations with it are optimized at the engine level.

Blogs

Book Recommendations