TypeScript Generics for Beginners: Generic Functions and extends Constraints
November 6, 2026
Welcome to episode 21 of TypeScript from Zero, a series for complete beginners (no JavaScript needed). Welcome back to TypeScript from zero! Today: generics. A generic is a type that is a placeholder, filled in later. It sounds hard, but it solves a very simple problem. We are in the shop project, with a new file called generics.ts. Every code block below is the exact code and output from the video, ready to copy.
- All episodes are in the TypeScript from Zero playlist.
- Versions used: Node.js 26.13.0, npm 11.20.0, TypeScript 7.0.2.
Setup
We start in the terminal and open the project in VS Code:
cd shop
touch src/generics.ts
code .1. A function
Here is a function that returns the first item of a list of numbers. We call it with three numbers, and it prints ten.
src/generics.ts:
function first(items: number[]) {
return items[0];
}
console.log(first([10, 20, 30]));$ node src/generics.ts
102. The problem
Now we want the first item of a list of words. Node runs it happily, and prints a. But tsc marks every word: type string is not assignable to type number. TS2322. Copying the function for every type is not the answer.
src/generics.ts:
function first(items: number[]) {
return items[0];
}
console.log(first([10, 20, 30]));
console.log(first(["a", "b"]));$ node src/generics.ts
10
a$ npx tsc --noEmit
src/generics.ts:5:20 - error TS2322: Type 'string' is not assignable to type 'number'.
5 console.log(first(["a", "b"]));
~~~
src/generics.ts:5:25 - error TS2322: Type 'string' is not assignable to type 'number'.
5 console.log(first(["a", "b"]));
~~~
Found 2 errors in the same file, starting at: src/generics.ts:53. Generic
The answer is a type parameter. We write T in angle brackets after the function name, and use T instead of number. T stands for any type. The function now works for numbers and for words, and tsc is quiet.
src/generics.ts:
function first<T>(items: T[]) {
return items[0];
}
console.log(first([10, 20, 30]));
console.log(first(["a", "b"]));$ npx tsc --noEmit4. Inference
We never tell tsc what T is. It looks at the argument and works it out. Numbers give a number, words give a string. If we want, we can also write the type ourselves in angle brackets, like first of string.
src/generics.ts:
function first<T>(items: T[]) {
return items[0];
}
console.log(first([10, 20, 30]));
console.log(first(["a", "b"]));
const n = first([10, 20, 30]);
const w = first(["a", "b"]);
console.log(n, w);
console.log(first<string>(["x", "y"]));$ node src/generics.ts
10
a
10 a
x5. Constraint
Sometimes T must be able to do something. Here we compare lengths, so the type needs a length property. We write T extends, and an object type with a length. Strings and arrays both have a length, so both work.
src/generics.ts:
function first<T>(items: T[]) {
return items[0];
}
console.log(first([10, 20, 30]));
console.log(first(["a", "b"]));
const n = first([10, 20, 30]);
const w = first(["a", "b"]);
console.log(n, w);
console.log(first<string>(["x", "y"]));
function longest<T extends { length: number }>(a: T, b: T) {
return a.length >= b.length ? a : b;
}
console.log(longest("apple", "fig"));
console.log(longest([1, 2, 3], [4, 5]));$ node src/generics.ts
10
a
10 a
x
apple
[ 1, 2, 3 ]6. Error
A number has no length, so this call is wrong. Node does not stop, and prints twenty, which is a silent bug. tsc says: argument of type number is not assignable to parameter of type with a length. TS2345 again.
src/generics.ts:
function first<T>(items: T[]) {
return items[0];
}
console.log(first([10, 20, 30]));
console.log(first(["a", "b"]));
const n = first([10, 20, 30]);
const w = first(["a", "b"]);
console.log(n, w);
console.log(first<string>(["x", "y"]));
function longest<T extends { length: number }>(a: T, b: T) {
return a.length >= b.length ? a : b;
}
console.log(longest("apple", "fig"));
console.log(longest([1, 2, 3], [4, 5]));
console.log(longest(10, 20));$ node src/generics.ts
10
a
10 a
x
apple
[ 1, 2, 3 ]
20$ npx tsc --noEmit
src/generics.ts:15:21 - error TS2345: Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.
15 console.log(longest(10, 20));
~~
Found 1 error in src/generics.ts:157. Fix
We delete that line, and tsc is quiet again.
src/generics.ts:
function first<T>(items: T[]) {
return items[0];
}
console.log(first([10, 20, 30]));
console.log(first(["a", "b"]));
const n = first([10, 20, 30]);
const w = first(["a", "b"]);
console.log(n, w);
console.log(first<string>(["x", "y"]));
function longest<T extends { length: number }>(a: T, b: T) {
return a.length >= b.length ? a : b;
}
console.log(longest("apple", "fig"));
console.log(longest([1, 2, 3], [4, 5]));$ npx tsc --noEmitRecap
Today: a generic function with a type parameter T, how tsc infers it, and the extends constraint. Next episode: generic types, and a typed result.