M Logo
๐Ÿ“ TypeScript from Zero ยท Part 21 of 50

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.

Setup

We start in the terminal and open the project in VS Code:

bash
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:

ts
function first(items: number[]) {
  return items[0];
}
console.log(first([10, 20, 30]));
text
$ node src/generics.ts
10

2. 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:

ts
function first(items: number[]) {
  return items[0];
}
console.log(first([10, 20, 30]));
console.log(first(["a", "b"]));
text
$ node src/generics.ts
10
a
text
$ 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:5

3. 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:

ts
function first<T>(items: T[]) {
  return items[0];
}
console.log(first([10, 20, 30]));
console.log(first(["a", "b"]));
bash
$ npx tsc --noEmit

4. 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:

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"]));
text
$ node src/generics.ts
10
a
10 a
x

5. 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:

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]));
text
$ 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:

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));
text
$ node src/generics.ts
10
a
10 a
x
apple
[ 1, 2, 3 ]
20
text
$ 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:15

7. Fix

We delete that line, and tsc is quiet again.

src/generics.ts:

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]));
bash
$ npx tsc --noEmit

Recap

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.

ยฉ 2026 All Rights Reserved By Mahmudul Alam