Type Inference
Type Inference
Section titled βType InferenceβWhat it means
Section titled βWhat it meansβTypeScript can often figure out a variableβs or functionβs type automatically from context, without an explicit annotation β this is type inference. It happens for variable initializers, function return values, and even more advanced cases like generic type arguments inferred from the arguments passed in.
Examples
Section titled βExamplesβlet count = 5; // inferred as `number`// count = "five"; // Error: string not assignable to number, even with no annotation!
const name = "Alice"; // inferred as the literal type "Alice" (const, narrower)let city = "Boston"; // inferred as the wider type `string` (let, can be reassigned)
function double(n: number) { return n * 2; // return type inferred as `number`}
const numbers = [1, 2, 3]; // inferred as number[]const mixed = [1, "two", 3]; // inferred as (number | string)[]
function firstOf<T>(items: T[]): T { return items[0]; // T is inferred from whatever array is passed in}const first = firstOf(["a", "b"]); // TypeScript infers T as string, first: stringCommon mistake
Section titled βCommon mistakeβAssuming inference always gives you the narrowest, most useful type β with let, TypeScript widens literal values to their general type (string, number) rather than the specific literal, which can matter for things like discriminated unions.
let status = "loading"; // inferred as `string`, NOT the literal "loading"
function setStatus(s: "loading" | "success" | "error") { /* ... */ }setStatus(status); // Error: `string` is not assignable to the specific literal union
// const infers the narrower literal type insteadconst status = "loading"; // inferred as the literal type "loading"setStatus(status); // fineQuick practice
Section titled βQuick practiceβ-
Does TypeScript require an explicit type annotation on every variable?
Answer
No β it infers the type from the initializer whenever possible, and explicit annotations are only needed when inference can't determine what you intend (e.g. an empty array, or a parameter with no default). -
Why might
let status = "loading"behave differently fromconst status = "loading"when passed to a function expecting a specific string literal union?Answer
letwidens the inferred type to the generalstringtype (since it can be reassigned);constkeeps the narrower literal type"loading", since it can never change. -
In
function firstOf<T>(items: T[]): T, how does TypeScript know whatTis when you callfirstOf(["a", "b"])?Answer
It infersTfrom the argument's type β since["a", "b"]isstring[], TypeScript infersT = stringwithout you specifying it explicitly.