Generics
A generic function has a type parameter, T: a placeholder for a type, filled in fresh by every call. Watch tsc work out T = number from first([10, 20]) and T = string from first(["a", "b"]), give n and s their exact types, and use that to catch s.toFixed(1) before it can crash. Then the types are erased and the program prints A.
5 quick questions
Pick an answer to see why. Your score saves with your progress.
01In function first<T>(items: T[]): T, what is T?
<T> declares a type parameter. Just as items stands for a value passed in, T stands for a type, chosen per call.
02What type does tsc give n in const n = first([10, 20])?
[10, 20] is a number[], so matching it to T[] gives T = number. first returns a T, so n is a number.
03Why does tsc reject s.toFixed(1) when s = first(["a", "b"])?
For this call T = string, so s is a string. tsc reports error TS2339: Property 'toFixed' does not exist on type 'string'.
04If first were written as firstAny(items: any[]): any, what would happen to s.toFixed(1)?
any switches checking off, so s would be any and nothing is checked. At run time JavaScript throws TypeError: s.toFixed is not a function.
05What happens to <T> in the first.js that tsc writes?
Types, including type parameters, are erased. function first<T>(items: T[]): T becomes function first(items).
Glossary
- Generic
- A function (or type, or class) written with a type parameter, so it works for many types while keeping each one exact.
- Type parameter
- A placeholder for a type, written in angle brackets, like the T in first<T>. Each use fills it in.
- Type argument
- The actual type that fills a type parameter, such as number in first<number>([10, 20]).
- Type inference
- tsc working out a type on its own, here T from the type of the argument passed in.
- T[]
- An array (list) whose items all have type T. number[] is a list of numbers.
- any
- A type that turns type checking off for a value. Anything is allowed, so mistakes are not caught.
- Constraint
- A limit on what a type parameter can be, written with extends, as in <T extends { length: number }>.
- Type erasure
- Removing all types, including type parameters, when tsc writes JavaScript.
Notes and sources
- 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.
- A generic can also limit what T may be, such as <T extends { length: number }> for "anything with a length". That is called a constraint.
- By default tsc still writes the .js file even when it finds type errors (unless noEmitOnError is on). The buggy file would then crash when run.
- TypeScript Handbook: Generics
- TypeScript Handbook: More on Functions, Generic Functions
- TypeScript Handbook: Everyday Types (arrays, any)
- TypeScript Handbook: The Basics, Erased Types
- TSConfig Reference: target
- MDN: Number.prototype.toFixed()
- MDN: String.prototype.toUpperCase()
- MDN: TypeError: "x" is not a function
Quiz Unlocks after the last step.