Section 1: Thinking in types
Inference and annotations
You do not need to annotate everything. TypeScript infers types from values, and good TypeScript code relies on inference most of the time.
let count = 0; // number
const name = "Mina"; // "Mina", a literal type, because const cannot change
const tags = ["a", "b"]; // string[]
const user = { id: 1, admin: false }; // { id: number; admin: boolean }
Hover over a variable in your editor to see what TypeScript inferred. Make this a habit. Most confusion with TypeScript comes from not knowing what type a value actually has.
Where to annotate
Annotate at boundaries, where information enters or leaves a piece of code:
- Function parameters: TypeScript cannot guess what callers will pass.
- Return types of exported functions: they document the contract and stop accidental changes.
- Values that start empty:
const ids: string[] = [], otherwise the array type is not useful. - Data from outside: API responses and parsed JSON.
Inside a function, let inference work:
function summary(prices: number[]): string {
const total = prices.reduce((a, b) => a + b, 0); // inferred as number
return `Total: ${total}`;
}
any, unknown and never
anyturns off checking for a value. It spreads: anything you get from ananyis alsoany. Avoid it except during migration.unknownmeans "we do not know yet". You can store anything in it, but you must check its type before using it. It is the safe choice for data from outside.neveris the type of something that cannot happen, such as the result of a function that always throws. It becomes useful for exhaustive checks later.
const data: unknown = JSON.parse(text);
if (typeof data === "object" && data !== null && "id" in data) {
// now TypeScript knows data has an id property
}
Excess property checks
When you pass an object literal directly to something with a declared type, TypeScript complains about extra properties, which catches typos such as colour instead of color. This check applies only to fresh object literals, which surprises people at first, so we explore it in the exercises.
Exercise: in the playground, remove annotations from a sample file one by one and check which ones inference can replace.