String Number and Boolean Types in TypeScript

TypeScript's string, number, and boolean types map directly to JavaScript primitives. Learn how to use each one, what values they accept, and common mistakes to avoid.

7 min read

TypeScript has three primitive types: string, number, and boolean. These types match JavaScript's primitive values directly, and each one accepts exactly the values you would expect from JavaScript while rejecting everything else.

The table below shows what each type accepts:

TypeAccepted valuesExample
stringText and template literals"hello"
numberIntegers, floats, NaN, Infinity42, 3.14
booleantrue or false onlytrue, false

TypeScript infers these types automatically when you assign a value:

typescripttypescript
let title = "Learn TypeScript"; // inferred: string
let score = 100;                // inferred: number
let isDone = false;             // inferred: boolean

You rarely need to annotate primitives on variables because the initial value already makes the type obvious to the compiler. For more on when to annotate, see type annotations in TypeScript.

The String Type

A string holds text. It accepts string literals, template literals, and the result of any expression that evaluates to text.

typescripttypescript
let firstName: string = "Alice";
let greeting = `Hello, ${firstName}!`;
let shout = firstName.toUpperCase();
 
console.log(greeting); // "Hello, Alice!"
console.log(shout);    // "ALICE"

The embedded expression inside the template literal runs at compile time as ordinary JavaScript once compiled, and TypeScript checks that whatever you embed there is a valid expression. All the usual JavaScript string methods work as expected, and TypeScript already knows what type each one returns:

typescripttypescript
let phrase = "TypeScript";
 
phrase.toLowerCase();     // returns string
phrase.includes("Type");  // returns boolean
phrase.indexOf("Script"); // returns number
phrase.slice(0, 4);       // returns string

Because TypeScript knows these return types, it also catches mistakes like calling a string method on a value that is not a string:

typescripttypescript
let count = 5;
count.toLowerCase();

The compiler recognizes that count is a number and that numbers do not have a toLowerCase method, so it rejects the call before the code runs instead of letting it crash at runtime.

texttext
Property 'toLowerCase' does not exist on type 'number'.

The Number Type

The number type covers all JavaScript numeric values. Unlike some languages, TypeScript does not have separate types for integers and floats; one type handles everything.

typescripttypescript
let integer: number = 42;
let float: number = 3.14;
let hex: number = 0xff;       // 255
let binary: number = 0b1010;  // 10
let big: number = 1_000_000;  // underscores for readability

The number type also includes the special values NaN and Infinity, and both are considered valid numbers at the type level:

typescripttypescript
let result: number = NaN;
let infinite: number = Infinity;
 
console.log(typeof result);   // "number"
console.log(typeof infinite); // "number"

TypeScript will not warn you that a variable might end up as NaN the way it warns about null or undefined under strict mode. You need to check for that case yourself at runtime with the Number.isNaN function.

All standard arithmetic produces a number, and TypeScript checks that both sides of an arithmetic operation are actually numeric:

typescripttypescript
let sum = 10 + 5;        // 15
let product = 10 * 5;    // 50
let quotient = 10 / 3;   // 3.3333...
let remainder = 10 % 3;  // 1

Mixing in a string breaks that expectation. JavaScript would coerce the string into a number at runtime, but TypeScript treats the multiplication operator as numeric only, so it catches the misuse before the code ever runs:

typescripttypescript
let count = 5;
count * "2";

The compiler rejects this expression outright instead of letting it silently coerce, which is exactly the kind of implicit conversion that causes confusing bugs in plain JavaScript.

texttext
The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.

TypeScript also knows the return types of Math functions and Number methods, so it can catch a wrong argument type the same way:

typescripttypescript
let price = 49.99;
 
Math.round(price);        // returns number
price.toFixed(2);         // returns string, "49.99"
Number.isInteger(price);  // returns boolean

The Boolean Type

A boolean is the simplest of the three primitives covered in this article. It accepts exactly two values, true and false, and the compiler assigns it automatically whenever a variable starts out holding one of those two values.

typescripttypescript
let isReady: boolean = true;
let hasErrors = false; // inferred: boolean

This is different from plain JavaScript, where truthy and falsy values can stand in for booleans inside a condition. TypeScript's boolean type is strict about the assignment itself, so a number like 1 cannot be assigned to a boolean variable even though it would behave as truthy in a condition:

typescripttypescript
let isReady: boolean = true;
isReady = 1;

Even though 1 behaves as truthy in a JavaScript condition, it is still a number, not a boolean, so the assignment itself fails at compile time:

texttext
Type 'number' is not assignable to type 'boolean'.

Comparison and logical operators produce boolean values automatically, so you rarely need to annotate a variable that receives the result of a comparison:

typescripttypescript
let isGreater = 10 > 5;            // true
let isEqual = "a" === "b";         // false
let isValid = password.length >= 8; // inferred: boolean

Avoid the Wrapper Types

JavaScript has wrapper objects, capitalized String, Number, and Boolean, that are different from the lowercase primitives. Never use these capitalized names as TypeScript types.

typescripttypescript
let wrong: String = "Alice"; // wrong: wrapper object type
let right: string = "Alice"; // right: primitive type

The wrapper types describe objects, not primitives, and they behave unexpectedly in comparisons because two wrapper objects with the same text are still different objects:

typescripttypescript
let a = new String("hello");
let b = new String("hello");
 
console.log(a === b); // false, different object instances

Always use the lowercase primitive names for annotations. There is no reason to reach for the capitalized wrapper types in ordinary application code, and most linters flag them automatically if they slip into a pull request.

Strict Mode and Null

With TypeScript's strict mode enabled, which is the recommended default for new projects, primitive types do not accept null or undefined:

typescripttypescript
let title: string = null;

The annotation says this variable always holds text, so strict mode treats null as a mismatch just like it would treat a number or a boolean, and stops the assignment before the file even compiles:

texttext
Type 'null' is not assignable to type 'string'.

This is one of the most valuable protections TypeScript provides. In plain JavaScript, a variable you expected to hold a string can silently become null and crash the program much later; strict mode stops that assignment at the source instead. If a value can genuinely be missing, say so explicitly with a union type that names null as one of the allowed possibilities:

typescripttypescript
let title: string | null = null; // ok: explicitly allows null

For more on handling nullable values, see null and undefined in TypeScript.

Common Mistakes

Expecting a Separate Integer Type

TypeScript does not distinguish between integers and floats the way some other languages do, so a beginner might expect the next line to fail and be surprised when it compiles cleanly:

typescripttypescript
let count: number = 3.7; // perfectly valid, TypeScript has no separate int type

If your logic depends on whole numbers only, validate that at runtime instead of relying on the type system to enforce it, since the type checker treats every numeric value the same way.

Trusting Unvalidated Input as a Number

Browser input is a common place beginners assume a value is already numeric, but the type system disagrees:

typescripttypescript
let age: number = prompt("How old are you?");

Browser APIs that read user input always return a string or null, never a number, even when the person types digits, so the annotation catches the mismatch immediately instead of letting a broken calculation slip through later:

texttext
Type 'string | null' is not assignable to type 'number'.

You need to parse and validate that input before treating it as a number. For more on this pattern, see runtime validation in TypeScript.

Rune AI

Rune AI

Key Insights

  • Use lowercase string, number, boolean for type annotations, never the capitalized wrappers.
  • number covers integers, floats, NaN, and Infinity.
  • TypeScript infers primitive types from initial values so you rarely need explicit annotations on variables.
  • Enable strict mode so null and undefined are not silently assignable to primitives.
  • Template literals, arithmetic, and logical operations all work exactly as they do in JavaScript.
RunePowered by Rune AI

Frequently Asked Questions

Should I use `String`, `Number`, or `Boolean` (capitalized) as types?

No. Always use lowercase `string`, `number`, and `boolean` for type annotations. The capitalized versions refer to JavaScript wrapper objects, which behave differently and can cause subtle bugs.

Does TypeScript distinguish between integers and floats?

No. The `number` type covers all numeric values: integers, floats, `NaN`, and `Infinity`. TypeScript does not have separate integer or float types like some other languages.

Can I assign `null` or `undefined` to a `string` variable?

Not when `strictNullChecks` is enabled (which it is in strict mode). Without strict null checks, `null` and `undefined` are assignable to any type. Always enable strict mode for safety.

Conclusion

String, number, and boolean are the three primitive types you will use in nearly every TypeScript file. They map directly to JavaScript primitives, accept exactly the values you expect, and are inferred automatically from initial values. Always use the lowercase versions for type annotations, never the capitalized wrapper objects. With strict mode enabled, these types keep your code safe from null, undefined, and accidental type mismatches.