Type inference
In the previous lesson you labeled every jar yourself. But the contents are often visible without a label. If buckwheat was poured into a jar, a helper can label it: they see what is inside and write "Buckwheat" for you.
In TypeScript the compiler is that helper. let count: number = 0 says "number" twice: once in the annotation and once more with the zero on the right. The zero is enough for the compiler. When the compiler works out a type from the code on its own—from a variable's initial value or from what a function returns—this is called type inference.
A type from the initial value
let count = 0;
let title = "Отчёт";
count = count + 1;
title = title + " за май";
console.log(count, title); // 1 Отчёт за майThere are no annotations here, but the variables do have types. Let's go through the example line by line:
let count = 0;—the right side is a number, and TypeScript infers the typenumberforcount.let title = "Отчёт";—the right side is a string, andtitlegets the typestring.count = count + 1;puts a number into anumber—the check passes.title = title + " за май";puts a string into astring—it passes too.console.logprints1 Отчёт за май.
An inferred type is checked as strictly as a written one. The line count = "two"; would give the same error TS2322 here as with the annotation let count: number = 0.
How to see an inferred type
The helper writes a label, but you don't see it in the code. To read it, hover over the name in an editor—VS Code or the TypeScript Playground. Over count the hover shows let count: number, over title—let title: string.
The hover is a handy way to check yourself. Before guessing a variable's type, hover over it: the hover shows what the compiler knows.
A function's result type
The helper reads not only initial values but also return:
function withDelivery(price: number) {
return price + 300;
}
const total = withDelivery(1200);
console.log(total); // 1500The function has no ): number annotation, but TypeScript sees the expression after return: price + 300 is a number plus a number, so the result is a number too. That is why total also gets the type number.
The type of the parameter price is written out here. A parameter without a default value has no initial value, and the helper has nothing to read.
A variable without a value
Sometimes a variable is declared without an initial value and filled in later:
let total;
total = 1200;
total = "бесплатно";
console.log(total); // бесплатноThere is nothing to read here, and the hover over total on the first line shows let total: any. any is a special "anything goes" type: such a variable takes a number and a string alike, and tsc says nothing.
Such a jar has no label: the helper only remembers what was poured in last. That is why the hover over total on the console.log line shows let total: string, while total still takes anything you write into it.
If the variable needs a type, write it yourself: with the declaration let total: number; the line total = "бесплатно"; gives error TS2322.
Interviewers often ask what type inference is. The short answer: the compiler works out a type from a variable's initial value and from a function's return. Where there is nothing to read, you write the type yourself.
Common mistake
Expecting the type to change together with the value, as in JavaScript:
let price = 1200;
price = "бесплатно"; // ✗error TS2322: Type 'string' is not assignable to type 'number'.TypeScript inferred the type of price once—from the initial value 1200—and the type does not change after that. Text for such a price goes into a separate variable, for example let priceLabel = "free";.
Key points
- Type inference: TypeScript works out a variable's type from its initial value—
let count = 0getsnumber. - An inferred type is checked as strictly as an annotation and does not change after the declaration.
- A function's result type is inferred from the expression after
return. - A variable without a value has the type
any, so you write the type of such a variable yourself:let total: number;.