Skip to content
Theory · Primitive and special types · lesson 1 of 8

Primitive types

What you'll learn

What you'll learn:

  • write the primitive types number, string, boolean, bigint and symbol;
  • explain why null does not fit the string type in strict mode;
  • tell any from unknown and check an unknown value before using it;
  • explain what void and never mean as a function's result type.
A parcel stamped any passes the inspection table, while the sealed box unknown is opened and holds a string

Let's go back to the customs border from the first theme. Every parcel travels with a declaration that says what is inside. The inspector compares the declaration with the contents and only then lets the parcel across the border. In TypeScript the declaration is a type, and the inspector is the compiler, tsc.

The simplest parcels carry a single value: a number, a piece of text, a "yes" or a "no". The types of such values are called primitive. You know the values themselves from JavaScript, and TypeScript adds a declaration to them:

TypeScript
const price: number = 199.9;
const title: string = "Kettle";
const inStock: boolean = true;
console.log(title, price, inStock); // Kettle 199.9 true

Let's walk through the example line by line:

  1. price is declared with the type number: it holds a number.
  2. title holds a string; its type is string.
  3. inStock holds a boolean value; its type is boolean.
  4. console.log prints all three values. The types do not affect the output: tsc erases them after the check.

If you put something into the box that the declaration does not name, such as the string "yes" in inStock, the inspector stops the parcel before the program even runs.

Five primitive types

Annotations use the same words that JavaScript's typeof operator returns:

  • number: any number, whole or fractional, plus NaN and Infinity;
  • string: strings;
  • boolean: two values, true and false;
  • bigint: whole numbers of any length, written with an n at the end: 10n;
  • symbol: unique values created by the Symbol function.

The last two types are rare in programs, but they are written the same way:

TypeScript
const big: bigint = 9007199254740993n;
const id: symbol = Symbol("id");
console.log(typeof big, typeof id); // bigint symbol

typeof prints the same words that stand in the annotations: bigint and symbol. You cannot mix bigint and number in one expression. The compiler rejects the expression big + 1, and JavaScript would throw a TypeError on such a line.

number stores whole numbers exactly only up to 9007199254740991, and the number in the example is bigger: as a number it would round to 9007199254740992. That is what bigint is for.

One number for all numbers

In many languages whole and fractional numbers are different types: int, float, double. In TypeScript, as in JavaScript, all ordinary numbers share one type, number. A variable let count: number = 3 happily accepts 2.5 too.

TypeScript has no int type at all. The annotation let count: int = 3 gives the error Cannot find name 'int': the compiler knows no such name.

Interviewers like to ask what the basic types in TypeScript are. A good answer names the five primitives, says that one number covers all numbers, and adds null and undefined. The next section is about them.

null and undefined are separate types

In JavaScript an empty value can go into any variable. The trouble comes later: if city holds null, the line city.toUpperCase() stops the program with TypeError: Cannot read properties of null (reading 'toUpperCase').

In strict mode, which this course always has on, null and undefined have types of their own, primitive ones too, which makes seven primitive types in all. The type null has one value, null, and the type undefined also has one, undefined. They do not fit other types:

TypeScript
const city: string = null; // ✗
TEXT
error TS2322: Type 'null' is not assignable to type 'string'.

A "text" declaration on an empty box is a lie, and the inspector does not let such a parcel through. In the same way, let count: number = undefined gives Type 'undefined' is not assignable to type 'number'. In return, you can trust a string variable: it always holds a string, and its methods never fail on null.

Note that typeof null gives "object" because of an old JavaScript mistake, while the TypeScript type is called null.

Here the customs picture stops working. A real inspector opens the box, while tsc sees only the code: it checks what is written in the program and never sees the values that arrive at run time.

Common mistake

The type was written with a capital letter:

TypeScript
function shout(text: string): string {
    return text.toUpperCase();
}
const greeting: String = "Hi";
console.log(shout(greeting)); // ✗
TEXT
error TS2345: 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.

String, Number and Boolean with a capital letter are the types of wrapper objects: the objects JavaScript creates with a call like new String("Hi"). TypeScript lets you write these types, but they do not fit where a string is expected. The second line of the message suggests the fix: write string in lowercase, and the call shout(greeting) passes the check.

Key points

  • Primitive types are written in lowercase: number, string, boolean, bigint, symbol, the same words typeof prints.
  • number is one type for whole and fractional numbers; TypeScript has no int or float.
  • In strict mode null and undefined are separate types, and string does not accept them.
  • String, Number and Boolean with a capital letter are wrapper-object types; annotations never use them.