Type inference with generics
What type inference means in the context of generics
When you call a function with a generic parameter, TypeScript analyzes the arguments passed and substitutes the type itself for T (or other parameters).
That is, you don't need to explicitly write <string>, <number>, and so on - the compiler does this automatically.
Example 1: basic type inference
function identity<T>(value: T): T {
return value;
}
const a = identity("Hello"); // T = string
const b = identity(42); // T = numberTypeScript sees that the argument "Hello" has type string,
and substitutes T = string automatically.
Example 2: inference from multiple arguments
function merge<T, U>(a: T, b: U): T & U {
return Object.assign({}, a, b);
}
const result = merge({ name: "Tim" }, { age: 25 });
// TypeScript infers T = { name: string }, U = { age: number }
// The result has type { name: string; age: number }Here T and U are inferred separately for each argument.
TypeScript combines them via &.
Example 3: inference from context (contextual inference)
function wrap<T>(value: T): { data: T } {
return { data: value };
}
const wrapped = wrap({ id: 1, name: "Tim" });
// T = { id: number; name: string }TypeScript "sees" the structure of the passed object and infers the exact type.
Example 4: partial inference
function pair<T, U = string>(first: T, second?: U): [T, U] {
return [first, second as U];
}
const p1 = pair(1); // T = number, U = string (default)
const p2 = pair(true, false); // T = boolean, U = booleanYou can set a default type for a generic parameter - it is then used when TypeScript cannot infer the type from the arguments.
Example 5: type inference on a function's return value
function makeArray<T>(item: T, times: number): T[] {
return Array(times).fill(item);
}
const arr = makeArray(5, 3); // T = number -> number[]The type T is inferred from the first argument, item.
Example 6: when inference does not work
function logValue<T>(value: T): void {
console.log(value);
}
logValue(null); // T cannot be inferred precisely
logValue(undefined); // the same appliesIn such cases the type must be specified explicitly:
logValue<string | null>(null);How inference works under the hood
TypeScript applies several strategies:
- From arguments to type parameters - analyzing the arguments and their types.
- From return type - when the result type can be computed.
- Contextual typing - analyzing the context in which the function is used.
An example of contextual inference:
const handler = <T>(value: T) => console.log(value);
["a", "b", "c"].forEach(handler);
// TypeScript understands that T = stringSummary
| What TS does | Example | Result |
|---|---|---|
| Infers the type from an argument | identity(42) | T = number |
| Infers the type from multiple arguments | merge({a: 1}, {b: 2}) | T = {a: number}, U = {b: number} |
| Applies the default type | pair(1) | U = string |
| Contextual inference | ["x"].forEach(handler) | T = string |
| Cannot infer the type | logValue(null) | must be specified explicitly |
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.