string, number, boolean and the rest
The type you write most is a primitive — string, number, boolean. They are the atoms of every
program, and TypeScript's handling of them is mostly what you would expect, with a few sharp edges
worth knowing from the start. This lesson is those, plus the special values null and undefined
that cause more crashes than everything else combined.
The three you use constantly
let city: string = "Pune";
let population: number = 3_500_000;
let isCapital: boolean = false;
string— text, in single or double quotes or backticks. All the string methods you know (toUpperCase,split,includes) are typed.number— every number, whole or decimal. Unlike some languages there is no separateintandfloat;numbercovers42,3.14,-7, all of it. (There is a distinctbigintfor integers beyond about 9 quadrillion, which you rarely need.)boolean—trueorfalse.
Two immediate notes. Underscores in number literals (3_500_000) are ignored and aid
readability. And in practice you rarely write these annotations at all — let city = "Pune" infers
string — but it is worth seeing them explicitly once so you know what inference is doing.
Casing matters — lowercase, not uppercase
A trap that catches everyone from other languages: the primitive types are lowercase. TypeScript
also has String, Number, Boolean (capitalised) — but those are the JavaScript wrapper-object
types, and you should almost never use them:
let a: string = "Pune"; // correct — the primitive
let b: String = "Pune"; // wrong — the wrapper object; avoid
String (capital) refers to the boxed object, which behaves subtly differently and is not what you
want. Always use the lowercase string, number, boolean. If you see a capitalised primitive
in code, it is a mistake. This is worth committing to memory now, because the error you get when you
mix them up is confusing.
null and undefined — the crash-causers
JavaScript has two "nothing" values, and they are behind the majority of runtime crashes:
undefined— a value that was never set. A variable declared but not assigned, a missing object property, a function that returns nothing.null— a value deliberately set to "nothing". You assignnullon purpose to mean "empty".
The distinction: undefined usually means "nobody set this"; null usually means "somebody set this
to empty on purpose". Both are their own types in TypeScript (undefined and null), and here is the
crucial part — how TypeScript handles them depends entirely on one setting:
// with strictNullChecks ON (which strict mode enables — always use it):
let name: string = null; // error: Type 'null' is not assignable to type 'string'
let name2: string | null = null; // fine — you explicitly allowed null
With strictNullChecks on (part of strict mode, which you should always enable — the next lessons
and the real-projects module insist on it), a string cannot hold null or undefined. If a value
might be absent, you must say so explicitly with a union: string | null. This is TypeScript's answer
to the billion-dollar mistake — the same idea Kotlin builds in — and it is one of the biggest reasons
to use TypeScript at all. The unions-and-narrowing module is largely about handling these safely.
Without strict null checks, null and undefined sneak into any type and you get JavaScript's crashes
back. So: turn on strict mode, and treat "might be null" as something you declare, not something
that happens by accident.
symbol and bigint, briefly
Two more primitives you will meet rarely:
bigint— for integers larger thannumbercan safely hold (beyond ~9 quadrillion). Written with annsuffix:9007199254740993n. You need it only for very large integer maths.symbol— a unique, unguessable value, used as an object key when you need one that cannot collide. Advanced; you can go a long way without writing one.
Know they exist so they do not surprise you; you will not reach for them in your first months.
Type annotations versus inference
You have now seen types written explicitly. In real code, you write them far less than you might expect, because inference (a full lesson soon) handles most locals:
let city = "Pune"; // inferred string — no annotation needed
const rate = 4.5; // inferred number
function area(r: number) { // annotate the PARAMETER (inference cannot guess it)
return 3.14159 * r * r; // return type inferred as number
}
The pattern that emerges across the whole course: annotate the boundaries — function parameters and
data coming from outside — and let TypeScript infer the interior. Over-annotating every local
variable is noise. But knowing what the types are — being able to read string, number,
boolean, string | null — is essential, because inference is filling them in whether you wrote them
or not.
Check your work
The three everyday primitives. string, number, boolean.
How number differs from many languages. One type for all numbers — no separate int/float.
Why lowercase, not uppercase. string/number/boolean are the primitives; String/Number/
Boolean are wrapper objects you should avoid.
null versus undefined. undefined usually means "never set"; null means "deliberately
empty".
Why they matter so much. They cause the majority of runtime crashes.
What strictNullChecks does. With it on, a string cannot hold null/undefined; you must
declare string | null to allow absence. Always turn strict mode on.
Two rarely-used primitives. bigint (very large integers, n suffix) and symbol (unique keys).
The annotation habit. Annotate boundaries (parameters, external data); let inference handle the interior.
Practice
- Declare a
string,number, andbooleanwith explicit annotations, then again with inference, and confirm (by hovering) the inferred types match. - Try
let b: String = "Pune"(capital) and read what your editor says. Switch to lowercase. - With strict mode on, try
let name: string = null. Read the error. Fix it withstring | null. - Assign
nullto astring | nulland then try to call.toUpperCase()on it. Note TypeScript stops you until you handle the null (a preview of narrowing). - Use
3_500_000and confirm the underscores do not change the value. - Annotate a function's parameter but not its return type, and confirm the return type is inferred correctly.
Official documentation
- TypeScript — Everyday Types: primitives — The three, and the lowercase rule.
- TypeScript — strictNullChecks — Why to enable it, and what it changes.
- TypeScript — bigint and symbol — The rarer primitives.
Next: arrays and tuples.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship