TypeScript Type Inference
Automatic type detection by TypeScript
🧠 What is Type Inference?
Type Inference is TypeScript's intelligent ability to automatically determine and assign types to variables, function returns, and expressions without requiring explicit type annotations, making code cleaner and more maintainable.
// TypeScript infers the type automatically
let message = 'Hello'; // inferred as string
let count = 42; // inferred as number
let isActive = true; // inferred as boolean
Output:
✅ Types automatically inferred without annotations
Key Type Inference Concepts
Variable Inference
Automatic type from initial value
let name = 'John';
// name is string
Return Type Inference
Function return types inferred
function add(a: number, b: number) {
return a + b; // returns number
}
Contextual Typing
Type inferred from context
window.onmousedown = (event) => {
// event is MouseEvent
};
Best Common Type
Infer from multiple values
let arr = [1, 2, 3];
// arr is number[]
🔹 Basic Type Inference
TypeScript automatically infers types from values:
// Primitive types
let name = 'Alice'; // string
let age = 25; // number
let isStudent = true; // boolean
let nothing = null; // null
let notDefined = undefined; // undefined
// Arrays
let numbers = [1, 2, 3]; // number[]
let names = ['Alice', 'Bob']; // string[]
let mixed = [1, 'two', true]; // (string | number | boolean)[]
// Objects
let person = {
name: 'John',
age: 30
};
// person is { name: string; age: number; }
// No need for explicit types!
let greeting = 'Hello';
// greeting.toUpperCase(); // TypeScript knows it's a string
Output:
✅ All types automatically inferred
🔹 Function Return Type Inference
TypeScript infers return types from function bodies:
// Return type inferred as number
function add(a: number, b: number) {
return a + b;
}
// Return type inferred as string
function greet(name: string) {
return `Hello, ${name}!`;
}
// Return type inferred as boolean
function isEven(num: number) {
return num % 2 === 0;
}
// Return type inferred as string | number
function getValue(condition: boolean) {
if (condition) {
return 'yes';
}
return 42;
}
// Return type inferred as void
function logMessage(msg: string) {
console.log(msg);
}
// Arrow functions also infer return types
const multiply = (x: number, y: number) => x * y; // returns number
const getName = () => 'John'; // returns string
🔹 Contextual Typing
Type inferred from the context where value is used:
// Event handler - event type inferred
window.onmousedown = function(event) {
// event is automatically MouseEvent
console.log(event.button);
};
// Array method callbacks
const numbers = [1, 2, 3, 4, 5];
// num is inferred as number
numbers.forEach(num => {
console.log(num.toFixed(2));
});
// item is inferred as number, returns boolean
const evens = numbers.filter(item => item % 2 === 0);
// value is inferred as number, returns number
const doubled = numbers.map(value => value * 2);
// Promise callbacks
fetch('/api/data')
.then(response => {
// response is inferred as Response
return response.json();
})
.then(data => {
// data is inferred as any (from json())
console.log(data);
});
🔹 Best Common Type
TypeScript finds the best common type from multiple values:
// Array with multiple types
let mixed = [1, 2, 3, 'four'];
// Type: (string | number)[]
// Array with objects
let items = [
{ name: 'Apple', price: 1.5 },
{ name: 'Banana', price: 0.8 }
];
// Type: { name: string; price: number; }[]
// Conditional expressions
let value = Math.random() < 0.5 ? 'hello' : 42;
// Type: string | number
// Class instances
class Dog {
bark() {}
}
class Cat {
meow() {}
}
let pets = [new Dog(), new Cat()];
// Type: (Dog | Cat)[]
// Null/undefined in arrays
let nullableNumbers = [1, 2, null, 4];
// Type: (number | null)[]
🔹 Type Inference with Generics
Generic types can be inferred from arguments:
// Generic function
function identity<T>(arg: T): T {
return arg;
}
// Type argument inferred as string
let output1 = identity('hello');
// Type argument inferred as number
let output2 = identity(42);
// Array generic inference
function getFirst<T>(arr: T[]): T {
return arr[0];
}
let firstNumber = getFirst([1, 2, 3]); // number
let firstName = getFirst(['a', 'b', 'c']); // string
// Multiple type parameters
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
let result = pair('hello', 42); // [string, number]
// Promise inference
async function fetchUser() {
return { id: 1, name: 'John' };
}
// Return type inferred as Promise<{ id: number; name: string; }>
let user = fetchUser();
🔹 When to Use Explicit Types
Sometimes explicit types are better than inference:
// ❌ Inference might be too broad
let value = null; // Type: null (not useful)
// ✅ Better with explicit type
let value: string | null = null;
// ❌ Unclear intent
let data = {}; // Type: {}
// ✅ Clear intent with explicit type
let data: { name: string; age: number } = {
name: 'John',
age: 30
};
// ❌ Function parameter needs type
function greet(name) { // Error: Parameter needs type
return `Hello, ${name}`;
}
// ✅ Explicit parameter type
function greet(name: string) {
return `Hello, ${name}`;
}
// ✅ Explicit return type for documentation
function calculateTotal(items: number[]): number {
return items.reduce((sum, item) => sum + item, 0);
}