Last 30 Days
No notifications
Before you build interfaces, generics, or React props, you need fluency with the building blocks: string, number, boolean, null, undefined, and the three special citizens β any, unknown, and never. Understanding when to annotate vs let TS infer, and the difference between literal and widened types, is what separates beginner TS from professional TS.
TypeScript's primitive types map 1-to-1 with JavaScript's:
| Type | Example | Notes |
string | "hi", \hello ${x}\`` | UTF-16 strings |
number | 42, 3.14, 0xff, Infinity, NaN | All numbers are 64-bit floats |
boolean | true, false | |
bigint | 9007199254740993n | Arbitrary-precision integers |
symbol | Symbol("id") | Unique runtime identifiers |
null | null | Intentional absence |
undefined | undefined | Default uninitialised value |
> Lowercase string/number/boolean are the primitive types. The capitalised String/Number/Boolean are the boxed wrapper objects β almost never what you want.
let firstName: string = "Rahul";
let age: number = 21;
let isAdmin: boolean = false;
let id: bigint = 100n;
let tag: symbol = Symbol("user");TypeScript infers types from initialisers. Don't annotate when inference is obvious β it's just visual noise.
const PI = 3.14; // inferred: 3.14 (literal)
let counter = 0; // inferred: number
const colours = ["red", "blue"]; // inferred: string[]// Annotate when there's no value to infer from:
let nextId: number;
function area(w: number, h: number): number { return w * h; }
const infers the *literal* value. let widens to the broader type.
const greeting = "hello"; // type is exactly "hello"
let salutation = "hello"; // type is stringtype Direction = "up"
"down" "left"
"right";
let move: Direction = "up"; // β
move = "diagonal"; // βThat's how libraries give you autocomplete on string options β they're literal-union types, not free-form strings.
as constTo freeze an object/array into a deeply readonly literal:
const config = {
endpoint: "/api",
retries: 3,
} as const;
// config: { readonly endpoint: "/api"; readonly retries: 3 }any, unknown, neverThese three are TypeScript's "escape hatches". Knowing the difference is the most common interview question.
any β opt out of the type systemAnything goes. TS will not check anything you do with it. Treat it as a code smell β every any is a place a runtime bug can hide.
let x: any = "hi";
x.toFixed(); // β
no errorβ¦ but crashes at runtimeunknown β the safe anyYou can assign anything to it, but you can't *do* anything with it until you narrow it.
function parse(input: unknown) {
// input.toFixed(); // β
if (typeof input === "number") {
return input.toFixed(2); // β
narrowed to number
}
return String(input);
}Rule: when you receive data from outside (JSON, fetch, localStorage), type it as unknown and validate.
never β the impossible typeA function that never returns (throws or loops forever) returns never. The exhaustive-check pattern uses it to make the compiler force you to handle every case.
function fail(msg: string): never {
throw new Error(msg);
}type Shape = { kind: "circle" } | { kind: "square" };
function area(s: Shape) {
switch (s.kind) {
case "circle": return 1;
case "square": return 2;
default:
const _exhaustive: never = s; // errors if you add a new shape and forget here
return _exhaustive;
}
}
null and undefinedWith strictNullChecks (part of strict), null and undefined are not assignable to other types. You must opt in:
let name: string = null; // β
let nick: string null = null; // β
function find(id: number): User
undefined { /* β¦ */ }
const u = find(1);
// u.name; // β Object is possibly 'undefined'
u?.name; // β
optional chainingasWhen *you* know more than the compiler, use as. Use sparingly β it's a promise the compiler can't verify.
const el = document.getElementById("app") as HTMLDivElement;
const data = JSON.parse(json) as User;Avoid the angle-bracket form β it clashes with JSX.
const num = "hi" as unknown as number; // π¨ you're lying β TS will let you, runtime will notIf you need this, you probably need a real validator (Zod, Valibot, io-ts).
type Status = "loading" "success"
"error";function describe(s: Status): string {
switch (s) {
case "loading": return "β³";
case "success": return "β
";
case "error": return "β";
}
}
describe("success"); // β
// describe("done"); // β not assignable to Status