The Primitive Types

Beginner · 17 min read · ▶ live playground · ✦ checkpoint

TypeScript's primitive types are the basic value sets: string, number, boolean, null, undefined, bigint, and symbol. Use the lowercase names in annotations; capitalized String, Number, and Boolean are wrapper object types, and they are almost always the wrong type.

A primitive type is a type whose values are single JavaScript values, not containers with named parts. If types are sets of allowed values, primitives are the small starter sets you reach for before arrays and objects enter the room.

The everyday three

You already know the three primitives you will write every day:

  • string is text: names, labels, slugs, messages.
  • number is JavaScript's regular numeric type: integers and decimals.
  • boolean is exactly two values: true and false.

Because annotations are promises, write the primitive name when a boundary needs a promise:

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

Inside the function, inference handles planLabel. At the boundary, the annotations say what callers must provide: text, a number, and a yes-or-no value.

Two flavors of nothing

null and undefined are separate primitive value sets. undefined means JavaScript has no value there yet or no value was provided. null is an explicit empty value you write on purpose.

That difference sounds small, but strict TypeScript treats both as real values, not as secret members of every other type:

const displayName: string = null;
const backupName: string = undefined;

→ playground.ts:1:7 - error TS2322: Type 'null' is not assignable to type 'string'.
→ playground.ts:2:7 - error TS2322: Type 'undefined' is not assignable to type 'string'.

Read that as set logic. The string set contains text values. It does not contain null, and it does not contain undefined. Later, optional properties are object parts that may be missing, and optional parameters are function inputs callers may omit; lessons 2.3 and 3.2 give you those shapes. For now, the rule is enough: if "missing" is possible, your type has to say so.

BigInt and symbol

You will not reach for bigint and symbol as often, but they are part of the primitive roster.

A bigint is an integer primitive for values that must stay exact beyond normal number limits. You write one with an n suffix, like 9007199254740993n. You meet it in places like large database ids, ledgers, or counters where rounding is not acceptable.

A symbol is a unique primitive token: even two symbols with the same description are different values. Most beginners meet symbols through library code before they design with them directly.

typeof is runtime inspection

typeof is JavaScript's runtime inspection operator: it returns a string that names the kind of value the program is holding right now. This is JavaScript running, not the type layer talking.

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

→ string
→ number
→ boolean
→ undefined
→ object
→ bigint
→ symbol
→ Symbol(session)

The fifth line is the old JavaScript surprise: typeof null prints "object". That is not TypeScript being clever. It is JavaScript keeping a 1995 behavior for compatibility. The final line calls JavaScript's String(...) function to print the symbol; that is runtime code, not a String type annotation and not new String(...). Lesson 4.2 uses typeof inside if statements to teach the checker about a value; here, treat it only as a runtime readout.

Lowercase types, never wrapper objects

The primitive type names are lowercase on purpose: string, number, boolean, bigint, symbol, null, undefined.

The capitalized names are different. A wrapper object is an object version of a primitive value, created by calls like new Number(3) or new String("Mina"). JavaScript has them for historical reasons. You almost never want their types.

Here is the trap in its smallest form:

const launchCount: Number = 3;
const nextLaunch = launchCount + 1;
console.log(nextLaunch);

→ playground.ts:2:20 - error TS2365: Operator '+' cannot be applied to types 'Number' and 'number'.

The capital Number annotation looked close, but it promised a wrapper object type. Normal arithmetic expects primitive number. The professional rule is boring and powerful: use lowercase primitive types unless a later lesson gives you a very specific reason not to.

That rule includes String, Boolean, Symbol, and BigInt. If you catch yourself typing a capital primitive name in an annotation, stop and make it lowercase.

Literal primitives are smaller sets

Lesson 2.1 showed that const plan = "pro" gets the literal type "pro", not the wider string. A literal type is a one-value set: "pro" contains only the exact text "pro", and 3 contains only the exact number 3.

Those are still primitive types. They are just smaller slices of the primitive sets. Lesson 4.1 will combine literal types into bigger models; for now, keep the simple picture: string is the whole text set, and "pro" is one value inside it.

Primitive values are the single cards. Next lesson, you start putting cards into larger shapes: arrays, tuples, and objects.

Checkpoint

Answer all three to mark this lesson complete

+50 XP on completion