Question
In TypeScript, what is the difference between String and string? At first glance they seem like they should represent the same kind of value, but this code behaves differently:
var a: String = "test";
var b: string = "another test";
a = b;
b = a; // compiler error
The compiler reports:
Type 'String' is not assignable to type 'string'.
'string' is a primitive, but 'String' is a wrapper object.
Prefer using 'string' when possible.
Why does this happen? Is it a bug, or is there an important difference between String and string in TypeScript?
Short Answer
By the end of this page, you will understand why string and String are not the same in TypeScript, how primitive values differ from wrapper objects, why the assignment error happens, and why developers almost always use lowercase string in real code.
Concept
In TypeScript, string and String represent related but different things.
stringis the primitive string type.Stringis the wrapper object type.
This mirrors JavaScript itself.
Primitive vs wrapper
JavaScript has primitive values such as:
stringnumberbooleanbigintsymbolnullundefined
A primitive string is a simple value:
let name: string = "Ada";
JavaScript also has object wrappers like:
StringNumberBoolean
These are object forms created with constructors:
let wrapped: String = new String("Ada");
That value is an object, not a primitive string.
Why this matters
TypeScript treats these differently because they behave differently at runtime.
let a = "hello";
let b = new String("hello");
console.log(typeof a); // "string"
console.log(typeof b); // "object"
Even though both can hold text, they are not the same type of value.
Why a = b works but b = a fails
A primitive string can be used where a String object-like type is expected, because JavaScript temporarily boxes primitives when accessing methods:
let text: string = "hello";
console.log(text.toUpperCase());
This works because JavaScript briefly treats the primitive like a String object behind the scenes.
But a String object is not the same as a primitive string, so TypeScript does not allow assigning it to a string variable.
The rule to remember
In TypeScript, prefer:
stringnumberboolean
Avoid using:
StringNumberBoolean
unless you specifically need the wrapper object type, which is rare.
Mental Model
Think of string as the actual text written on a note, and String as a plastic box that can hold that note.
string= the text itselfString= an object container around the text
If someone asks for a box-like thing, plain text can often still be handled because JavaScript can temporarily wrap it. But if someone asks for plain text, handing over a box is not the same thing.
That is why this feels one-way:
stringcan often act likeStringStringcannot safely act likestring
For everyday programming, you usually want the text itself, not the box.
Syntax and Examples
Core syntax
let primitiveText: string = "hello";
let wrappedText: String = new String("hello");
Recommended usage
Use lowercase string for variables, parameters, return types, and properties.
function greet(name: string): string {
return `Hello, ${name}`;
}
Example: assignment difference
let a: String = "test";
let b: string = "another test";
a = b; // OK
b = a; // Error
Why?
bis a primitive string.ais allowed to hold values compatible with the wrapper type.- But
amight contain an actualStringobject, which is not a primitive.
Example: runtime difference
let x: string = "abc";
let y: String = new String("abc");
console.log(typeof x); // "string"
console.log(typeof y); // "object"
console.log(x === y); // false
Even though they look similar, they are different kinds of values.
Best practice
// Good
let title: string = "TypeScript Guide";
// Avoid
let titleObject: String = new String("TypeScript Guide");
Step by Step Execution
Consider this example:
let a: String = "test";
let b: string = "another test";
a = b;
b = a;
Step 1: let a: String = "test";
ais declared as typeString.- The value assigned is a primitive string:
"test". - TypeScript allows this because primitive strings can be treated like string objects in many situations.
Step 2: let b: string = "another test";
bis declared as primitive typestring.- This is the normal and recommended way to store text.
Step 3: a = b;
bcontains a primitive string.- A primitive string is acceptable for
a. - So this assignment succeeds.
Step 4: b = a;
ais typed asString.- A variable of type
Stringcould contain a wrapper object such as:
new String("hello")
- But
bexpects a primitivestringonly. - Since not every
Stringvalue is a primitive string, TypeScript rejects the assignment.
A clearer version
let wrapped: String = new String("hello");
let primitive: string = wrapped; // Error
This makes the problem easier to see: an object is not assignable to a primitive.
Real World Use Cases
Where this matters in practice
Function parameters
Most functions should accept primitive strings:
function formatUsername(name: string): string {
return name.trim().toLowerCase();
}
API data
Data from APIs, forms, JSON, and databases is typically treated as primitive strings.
type User = {
email: string;
role: string;
};
Validation
When validating input, you usually check for primitive strings:
function isNonEmpty(value: string): boolean {
return value.length > 0;
}
Avoiding surprising behavior
Wrapper objects can behave unexpectedly in comparisons and truthiness.
const value = new String("");
if (value) {
console.log("This runs because objects are truthy");
}
That can be confusing if you expected empty text behavior.
Real Codebase Usage
In real TypeScript codebases, developers almost always use lowercase primitive types.
Common patterns
Type annotations
type Config = {
appName: string;
apiUrl: string;
};
Function contracts
function parseSlug(slug: string): string[] {
return slug.split("-");
}
Guard clauses
function printMessage(message: string) {
if (message.length === 0) {
return;
}
console.log(message);
}
Validation and normalization
function normalizeEmail(email: string): string {
return email.trim().toLowerCase();
}
What you usually do not see
function normalizeEmail(email: String): String {
return email;
}
This is avoided because wrapper types make typing less precise and can introduce runtime confusion.
Practical rule for codebases
- Use
stringin interfaces, types, classes, and function signatures. - Avoid
new String(...). - Avoid annotating values as
Stringunless you truly need wrapper-object behavior.
Common Mistakes
1. Using String instead of string
Broken example:
function greet(name: String): String {
return "Hello, " + name;
}
Better:
function greet(name: string): string {
return "Hello, " + name;
}
Why avoid it?
Stringmeans object wrapper type.- It is not the standard choice for text values.
2. Creating wrapper objects with new String()
Broken or confusing example:
const name = new String("Ada");
console.log(typeof name); // object
Better:
const name = "Ada";
console.log(typeof name); // string
3. Assuming equality behaves the same
const a = "hello";
const b = new String("hello");
console.log(a === b); // false
Why? Because one is a primitive and the other is an object.
4. Forgetting that wrapper objects are truthy
const value = new String("");
if (value) {
console.log("Runs even though the wrapped text is empty");
}
All objects are truthy in JavaScript.
5. Using capitalized primitive wrapper types for other values too
This same rule applies to:
numbervsNumberbooleanvsBoolean
Prefer the lowercase primitive types in TypeScript.
Comparisons
| Concept | string | String |
|---|---|---|
| Kind | Primitive type | Wrapper object type |
| Typical usage | Everyday text values | Rare, mostly avoided |
Runtime typeof | "string" | "object" |
| Created with | String literal like "abc" | new String("abc") |
| Recommended in TypeScript | Yes | Usually no |
| Safe for APIs/interfaces | Yes | Usually no |
| Can cause assignment issues | Less often | Yes |
Related comparison: number vs Number
| Primitive | Wrapper |
|---|---|
number | Number |
boolean | Boolean |
string | String |
The same best practice applies: use the lowercase primitive type unless there is a very specific reason not to.
Cheat Sheet
Quick rules
- Use
stringfor normal text values. - Avoid
Stringin type annotations. - Avoid
new String(). stringis primitive.Stringis an object wrapper.
Syntax
let a: string = "hello";
let b: String = new String("hello");
Assignment behavior
let x: String = "hi"; // OK
let y: string = x; // Error if x is typed as String
Runtime difference
typeof "hello" // "string"
typeof new String("hello") // "object"
Best practice
function getName(): string {
return "Ada";
}
Avoid
function getName(): String {
return new String("Ada");
}
Similar pairs
stringvsStringnumbervsNumberbooleanvsBoolean
Rule of thumb
If you are writing TypeScript application code, choose the lowercase primitive type.
FAQ
Is String the same as string in TypeScript?
No. string is a primitive type, while String is the wrapper object type.
Should I ever use String in TypeScript?
Usually no. In normal code, use string. Wrapper object types are rarely needed.
Why does a = b work but b = a fail?
Because a primitive string can be treated like a string object in some contexts, but a String object is not guaranteed to be a primitive string.
Is this a TypeScript bug?
No. It matches JavaScript's runtime behavior and TypeScript's type system.
Why can I call string methods on a primitive?
JavaScript temporarily boxes primitive strings so methods like .toUpperCase() work.
Does this apply to Number and Boolean too?
Yes. Prefer number over Number and boolean over Boolean.
What should I use in interfaces and function parameters?
Use string almost all the time.
Mini Project
Description
Build a small TypeScript utility that accepts a user name, validates it, and formats a greeting. The goal is to practice using the primitive string type everywhere and to avoid accidental use of the String wrapper object. This reflects how text is handled in real applications such as forms, APIs, and configuration values.
Goal
Create a greeting helper that safely works with primitive strings and demonstrates why string is the correct type choice.
Requirements
- Create a function that accepts a user name as a primitive string.
- Trim extra whitespace from the input.
- Return a default greeting when the trimmed name is empty.
- Return a formatted greeting when a valid name is provided.
- Do not use
Stringornew String()anywhere in the solution.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.