Skip to content

Type Inference

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.

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: string

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 instead
const status = "loading"; // inferred as the literal type "loading"
setStatus(status); // fine
  1. Does TypeScript require an explicit type annotation on every variable?

    AnswerNo β€” 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).
  2. Why might let status = "loading" behave differently from const status = "loading" when passed to a function expecting a specific string literal union?

    Answerlet widens the inferred type to the general string type (since it can be reassigned); const keeps the narrower literal type "loading", since it can never change.
  3. In function firstOf<T>(items: T[]): T, how does TypeScript know what T is when you call firstOf(["a", "b"])?

    AnswerIt infers T from the argument's type β€” since ["a", "b"] is string[], TypeScript infers T = string without you specifying it explicitly.