TypeScript — Fundamentals
Basic types — describing what a value actually is
TypeScript's primitive types map directly onto JavaScript's own: string, number, boolean, null, undefined, bigint, symbol. The difference is you can now say what a variable is supposed to hold, and the compiler holds you to it.
Type inference — TypeScript is often smarter than you think
You don't need to annotate every single variable. When TypeScript can figure out the type from the initial value, it infers it automatically — and treats that inferred type exactly as strictly as if you'd written it yourself.
The practical rule most real codebases follow: let inference handle local variables with an obvious initial value; write explicit annotations on function parameters (inference can't guess what a caller will pass) and on anything whose type isn't obvious from context.
Interfaces vs. type aliases — describing object shapes
Both describe the shape of an object. Think of an interface as a contract a class or object can be checked against, and a type alias as a name you're giving to any type — including things that aren't objects at all.
Union and intersection types — combining types deliberately
A union (|) means "this value is one of these types." An intersection (&) means "this value must satisfy all of these types at once."
Arrays and tuples — ordered collections, with and without fixed shape
An array type says "a list of this one type, any length." A tuple says "a fixed-length list, where each position has its own specific type" — genuinely different guarantees.
A common real use for tuples: React's useState returns a tuple ([value, setValue]), specifically so that destructuring gets each element's correct, distinct type rather than a generic union of "either could be either."
Enums — a named set of constant values
An enum gives a set of related constant values readable names, instead of scattering "magic strings" or numbers through a codebase.
In practice, many real codebases prefer a union of string literals (type OrderStatus = "pending" | "shipped" | "delivered") over enum — it compiles to nothing extra at runtime (true type erasure), while a numeric enum actually generates a small runtime object. Both are valid; know that this tradeoff exists rather than defaulting to enum automatically.
Functions with typed parameters and return values
Annotate a function's parameters (required — TypeScript can't guess what a caller will pass) and let it infer the return type, or annotate it explicitly when you want the compiler to hold you to a specific contract even if the implementation changes.
Try It (2 Minutes)
Open the [TypeScript Playground](https://www.typescriptlang.org/play) and write an interface for a Book with title: string, pages: number, and an optional subtitle?: string. Then write a function describeBook(book: Book): string that returns a sentence using those fields. Try calling it with an object missing title — watch the compiler catch it before you ever run the code.

