Write a Generic identity and map Without Losing Types
Turn two any-typed helpers into generic functions and see what the compiler now catches. After this, a typo or a wrong method on the result is a compile error.
Most "helper" functions start life with any: take a value, return a value, hope. This is a ten-minute exercise to turn two of them into generic functions, identity and map, and to see the difference in what the compiler tells you afterwards. If you want the ideas behind the syntax first, the lesson on generic functions in the course covers them. This page is only the recipe.
Step 1: write the any version and see what it costs
function identity(value: any): any {
return value;
}
function map(items: any[], fn: (item: any) => any): any[] {
const out: any[] = [];
for (const item of items) {
out.push(fn(item));
}
return out;
}
const word = identity("hello");
const lengths = map(["a", "bb"], (s) => s.lenght);
console.log(word.toFixed(2), lengths);
This compiles with no errors. There are two bugs in it. word is a string and toFixed does not exist on strings. And s.lenght is a typo. Both pass, because every any switches the checks off for the next line.
Step 2: add a type parameter to identity
Write <T> after the name, and use T for the parameter and the return type.
function identity<T>(value: T): T {
return value;
}
const word = identity("hello");
console.log(word.toFixed(2));
identity.ts(6,18): error TS2551: Property 'toFixed' does not exist on type '"hello"'. Did you mean 'fixed'?
The compiler now knows what word is. The message calls it "hello" rather than string, because it is a const and the exact text is its type. You did not write the type at the call. It was worked out from the argument.
Step 3: make map generic with two parameters
The input items have one type and the callback may return another, so you need two names: T for the input and U for the output.
function map<T, U>(items: T[], fn: (item: T) => U): U[] {
const out: U[] = [];
for (const item of items) {
out.push(fn(item));
}
return out;
}
const lengths = map(["a", "bb"], (s) => s.lenght);
const doubled = map([1, 2, 3], (n) => n * 2);
const labels: string[] = doubled;
map.ts(9,43): error TS2551: Property 'lenght' does not exist on type 'string'. Did you mean 'length'?
map.ts(11,7): error TS2322: Type 'number[]' is not assignable to type 'string[]'.
Type 'number' is not assignable to type 'string'.
Two things to check. The typo lenght is caught, because s is known to be a string. And doubled is number[], so putting it in a string[] is an error. Fix the typo and look at the working version.
function map<T, U>(items: T[], fn: (item: T) => U): U[] {
const out: U[] = [];
for (const item of items) {
out.push(fn(item));
}
return out;
}
console.log(map(["a", "bb"], (s) => s.length));
console.log(map([1, 2, 3], (n) => n * 2));
console.log(map([1, 2, 3], (n) => `#${n}`));
[ 1, 2 ]
[ 2, 4, 6 ]
[ '#1', '#2', '#3' ]
The third call turns numbers into strings, and the result is a string[]. That is U changing with the callback.
Step 4: look at what the compiler decided
In your editor, hover over map in a call. It shows the signature with the parameters replaced, something like map<number, string>(...). If you see any or unknown where you expected a real type, inference failed, and the fix is usually in the argument you passed.
⚠ Warning: Do not write return value as T in a generic function to make an error go away. If the body needs a cast to match the signature, the signature promises more than the body delivers.
Common mistakes
- Using
Tfor both the input and the output ofmap. Then the callback cannot change the type. - Adding
<T>to a function whereTappears only once. - Passing the type by hand on every call. Let the compiler infer it, and write it only for an empty array like
map<string, number>([], ...).
Related course: TypeScript Intermediate.
About
Turn two any-typed helpers into generic functions and see what the compiler now catches. After this, a typo or a wrong method on the result is a compile error.
- Responsible
- Jay P
- Last Update
- 10/12/2026
- Reading Time
- 4 min
Reviews
Sign in to write a reviewComments (0)
Sign in to commentNo comments yet.