Hint
Generic functions infer T at the call site; generic types require you to pass T explicitly (or let the constructor infer)
Both use type parameters, but they're instantiated at different points and in different ways.
Generic functions — T is inferred from the argument types at each call:
// Arrow function generic
const wrap = <T>(value: T): { value: T } => ({ value });
const wrapped = wrap(42); // T inferred as number
const wrapped2 = wrap('hello'); // T inferred as string
// Multiple type params
function zip<A, B>(a: A[], b: B[]): [A, B][] {
return a.map((item, i) => [item, b[i]]);
}
const pairs = zip([1, 2], ['a', 'b']); // [number, string][]
Generic types/interfaces — T must be provided when using the type:
type Box<T> = { value: T; label: string };
const numBox: Box<number> = { value: 42, label: 'count' };
// Must specify — TS can't infer from usage alone
// Generic class — T provided at instantiation or inferred from constructor
class Queue<T> {
private items: T[] = [];
enqueue(item: T): void { this.items.push(item); }
dequeue(): T | undefined { return this.items.shift(); }
}
const q = new Queue<string>(); // explicit
const q2 = new Queue(); // T inferred as unknown without explicit arg
q.enqueue('hello');
q2.enqueue(42); // T inferred as number from first enqueue
<T> explicitly — TypeScript figures it out. Only specify the type parameter when inference fails or you want to constrain the result type.