The idea
first returns the first item of a list. It should work for a list of numbers, a list of text, a list of anything. Without generics you write one copy per type, or use any and lose type checking.
<T> adds a type parameter: a placeholder for a type, just as items is a placeholder for a value. items: T[] means a list of T, and ): T means first returns a T. Each call fills T in.
Each call fills in T
tsc checks line 4, first([10, 20]). The list holds numbers, so its type is number[]. Matching number[] to T[] gives T = number. TypeScript infers this from the argument: you do not have to write it.
first returns a T, and for this call T is number, so n gets the type number. Hover over n in an editor and it shows const n: number. The type went in through items and came out through the result.
Line 5 calls the same function with ["a", "b"], a string[]. This call gets its own T: T = string. One function, two calls, two different types, and still only one copy of the code.
This time first returns a string, so s has the type string. With any instead of T, s would be any, and tsc would stop checking everything you do with it.
Bugs caught
Line 6 calls s.toFixed(1), but toFixed is a number method. tsc knows s is a string, so it reports: Property 'toFixed' does not exist on type 'string'. Run as plain JavaScript, it would crash with a TypeError.
You change line 6 to s.toUpperCase(), a real string method. tsc checks again: s is a string, strings have toUpperCase, so there are no errors.
Run it
tsc writes plain JavaScript with <T>, T[] and the other types removed: they exist only for the check. Node.js runs it. first gives "a", and toUpperCase turns it into A.
What happens
;; step 1/9 · why
first.ts (TypeScript)
> 1 function first<T>(items: T[]): T {
> 2 return items[0];
3 }
4 const n = first([10, 20]);
5 const s = first(["a", "b"]);
6 console.log(s.toFixed(1));
without generics:
firstNumber(items: number[]): number
firstString(items: string[]): string a copy per type
firstAny(items: any[]): any checking switched off
You can also write T yourself: first<number>([10, 20]). Usually you leave it out and tsc infers it from the arguments, as here. The first.js shown is what tsc 5.9 writes with --target es2022. With no settings tsc 5.9 targets old JavaScript (ES5) and writes var instead of const; the types are removed either way.