Welcome back! I am Mihir, and in this lesson we will learn Type Inference in TypeScript.
In the previous lesson, we learned how to manually write types using type annotations. But TypeScript is smart. You do not need to write types everywhere.
Type inference means TypeScript automatically figures out the type of a value based on how you write your code.
What is Type Inference?
Type inference is TypeScript's ability to automatically detect a type without an explicit annotation.
Example:
let course = "TypeScript";We did not write:
let course: string = "TypeScript";But TypeScript still understands that course is a string.
So this gives an error:
course = 100;TypeScript inferred course as string, so assigning a number is not allowed.
Inference with Variables
let name = "Mihir";
let age = 25;
let isTeacher = true;TypeScript infers:
| Variable | Inferred Type |
|---|---|
name | string |
age | number |
isTeacher | boolean |
This means the following code is invalid:
age = "twenty five";
isTeacher = "yes";TypeScript already knows the intended types.
const Inference vs let Inference
let and const are inferred slightly differently.
let
let role = "admin";TypeScript infers:
stringBecause let values can change.
role = "editor";This is allowed.
const
const role = "admin";TypeScript infers:
"admin"This is a literal type, because a const primitive value cannot be reassigned.
So TypeScript knows the value is exactly "admin", not just any string.
Inference with Arrays
const scores = [90, 85, 100];TypeScript infers:
number[]This is allowed:
scores.push(95);This is not allowed:
scores.push("excellent");Because the array is inferred as an array of numbers.
Mixed Arrays
const values = [1, "two", true];TypeScript infers:
(string | number | boolean)[]That means each item can be a string, number, or boolean.
Usually, clean arrays with one type are easier to work with.
Inference with Objects
const user = {
name: "Mihir",
age: 25,
isAdmin: true,
};TypeScript infers this shape:
{
name: string;
age: number;
isAdmin: boolean;
}So this works:
console.log(user.name.toUpperCase());But this does not:
console.log(user.email);TypeScript warns because email does not exist on the inferred object type.
Inference with Function Return Types
TypeScript can infer return types.
function add(a: number, b: number) {
return a + b;
}TypeScript infers that add returns number.
So:
const result = add(10, 20);result is inferred as number.
Another example:
function getGreeting(name: string) {
return `Hello, ${name}`;
}TypeScript infers the return type as string.
Function Parameters Are Different
TypeScript usually cannot infer function parameter types from just the function declaration.
function greet(name) {
return `Hello, ${name}`;
}If strict mode is enabled, TypeScript warns:
Parameter 'name' implicitly has an 'any' type.So function parameters should usually be annotated:
function greet(name: string) {
return `Hello, ${name}`;
}TypeScript can infer the return type, but it often needs help with parameters.
Contextual Typing
Sometimes TypeScript infers types from context.
Example:
const numbers = [1, 2, 3];
numbers.map((number) => {
return number * 2;
});We did not write:
(number: number)TypeScript knows number is a number because numbers is a number[].
This is called contextual typing.
Another example:
const names = ["Mihir", "Aarav", "Riya"];
names.forEach((name) => {
console.log(name.toUpperCase());
});TypeScript knows name is a string.
Inference with Destructuring
const product = {
title: "Keyboard",
price: 1499,
};
const { title, price } = product;TypeScript infers:
titleisstringpriceisnumber
This also works in function callbacks:
const products = [
{ title: "Keyboard", price: 1499 },
{ title: "Mouse", price: 799 },
];
products.forEach(({ title, price }) => {
console.log(`${title}: ${price}`);
});TypeScript understands the destructured values from the array item shape.
Annotation vs Inference
Both are useful.
Use inference for simple obvious values:
const name = "Mihir";
const age = 25;
const active = true;Use annotations when you want to communicate intention:
let selectedUserId: number;Use annotations for function parameters:
function calculateTotal(price: number, quantity: number) {
return price * quantity;
}Use custom types for object contracts:
type User = {
id: number;
name: string;
};
function printUser(user: User) {
console.log(user.name);
}Best Practice: Let TypeScript Infer When It Is Clear
This is too verbose:
const name: string = "Mihir";
const age: number = 25;
const isActive: boolean = true;This is clean:
const name = "Mihir";
const age = 25;
const isActive = true;The types are obvious, so inference is better.
But this should be annotated:
function createUser(name: string, email: string) {
return {
name,
email,
};
}Function parameters are part of your function's public contract.
Common Mistake: Empty Arrays
Empty arrays need careful handling.
const users = [];Depending on TypeScript settings and usage, this can become too loose or not useful.
Better:
type User = {
id: number;
name: string;
};
const users: User[] = [];Now TypeScript knows this array should contain users.
users.push({ id: 1, name: "Mihir" });Invalid:
users.push({ id: "one", name: "Mihir" });Common Mistake: null Values
let selectedUser = null;TypeScript may infer this too narrowly as null.
Better:
type User = {
id: number;
name: string;
};
let selectedUser: User | null = null;Now selectedUser can be either:
- a
User null
This is common in React state and API loading flows.
Quick Reference Summary
| Code | Inferred Type |
|---|---|
let name = "Mihir" | string |
const name = "Mihir" | "Mihir" |
let age = 25 | number |
let active = true | boolean |
const scores = [1, 2, 3] | number[] |
const values = [1, "two"] | (string | number)[] |
function add(a: number, b: number) { return a + b } | return type is number |
Practice
Look at this code and guess the inferred types:
const course = "TypeScript";
let duration = 30;
const tags = ["typescript", "javascript", "web"];
function getCourseTitle(title: string) {
return title.toUpperCase();
}Answers:
courseis"TypeScript"durationisnumbertagsisstring[]getCourseTitlereturnsstring
What You've Learned
You now understand:
- What type inference means
- How TypeScript infers variables
- Difference between
letandconstinference - How arrays and objects are inferred
- How function return types are inferred
- Why function parameters usually need annotations
- What contextual typing means
- When to use inference vs annotations
- Why empty arrays and
nullvalues often need explicit types
What's Next?
In the next lesson, we will learn Basic Types.
We will go deeper into string, number, boolean, null, undefined, any, unknown, never, and void.
Need Help?
- Have questions, confusion, or want to know more? Contact me
Good TypeScript code uses a balance: annotate where it improves clarity, and let inference work where the type is obvious.