031 — Learn TypeScript Generics in 13 Minutes (same type within, any type across)
Full companion narrative for Learn TypeScript Generics in 13 Minutes — why any erases and unions lie, generic functions with inference, Map and querySelector sightings, ApiResponse types, defaults, and constraints
Video: 031 · Learn TypeScript Generics In 13 Minutes · watch · ~12:51 · Web Dev Simplified (Kyle)
Source: this is a written companion to the video. Part of the Favorite TypeScript Videos curated path, module 04 (functions and generics). A direct complement to 018 — Generics: every feature here is motivated by a concrete failure of the simpler alternative first.
the one generics lesson in this batch
This is the only video in this batch that teaches TypeScript itself, and it lands squarely on the generics beat: the complete arc from “why not any” through generic functions, multi-parameter and nested generics, generic types, defaults, and constraints — in 13 minutes. The pedagogy is the asset: every feature is motivated by a concrete failure of the simpler alternative first. Mine it for the teaching sequence and the getFirstElement / ApiResponse examples, which transfer directly to lesson plans.

the motivating failure: any erases, unions lie
Starting point is getFirstElement(arr: number[]) returning the first item — correctly inferred as number. Passing a string array errors (it only accepts numbers). Two failed fixes follow: typing the parameter as any[] makes the return any (“obviously not very useful”); the union-typed version returns string | number even when the input is provably one side (“TypeScript’s not smart enough to know that”). The rule that falls out: whenever a function has data whose type should track what was passed in or returned, that is the perfect case for a generic.
// failure 1: any erases the return type
function getFirstElement(arr: any[]) {
return arr[0]; // any — useless to callers
}
// failure 2: union widens the return type
function getFirstElement2(arr: number[] | string[]) {
return arr[0]; // string | number — even for a number[] input
}
generic functions: declare <T>, let inference work
The fix: angle brackets after the function name declare a type parameter — conventionally T, but the presenter recommends descriptive names (ElementType) when learning. The parameter is “literally anything, but the exact same thing everywhere inside this function,” so arr: ElementType[] returning ElementType preserves input-to-output tracking: hovering shows number for the numbers call, string for the strings call. Multiple parameters comma-separate (<T, U>); nesting composes (Map<string, Map<string, number>>). Manual specification (fn<Type>(...)) exists for when inference fails — already familiar from document.querySelector<HTMLInputElement>(...) and array.map, which are generic functions learners have used unknowingly.
function getFirstElement<ElementType>(arr: ElementType[]): ElementType {
return arr[0];
}
const n = getFirstElement([1, 2, 3]); // number — inferred
const s = getFirstElement(["a", "b"]); // string — inferred
const manual = getFirstElement<number>([1, 2, 3]); // explicit when needed
generics in the wild: Map, Set, querySelector
Standard-library sightings that prove generics are already in the learner’s code: new Map<string, number>() (key type, value type — set then enforces string-key/number-value), with inference from constructor arguments when defaults are passed; array.map inferring its return generic from the callback. These double as the multi-parameter syntax demo (Map<K, V> is just two comma-separated parameters).
const m = new Map<string, number>();
m.set("score", 42); // ok
// m.set(42, "score"); // error: key must be string, value number
const names = [1, 2, 3].map((n) => n.toString()); // string[] — inferred
generic types: ApiResponse<TData> and aliases
Types take parameters too, with angle brackets after the type name. The vehicle is ApiResponse: { data: any; isError: boolean } suffers the same erasure as the function case, so any becomes TData and call sites must supply the argument explicitly (types cannot infer — “it’s not smart enough to do that”): ApiResponse<{ name: string; age: number }>. Readability aliases follow: type UserResponse = ApiResponse<{...}>, type BlogResponse = ApiResponse<{ title: string }> — one generic shape, many narrowed endpoints, with missing fields erroring until supplied.
type ApiResponse<TData> = {
data: TData;
isError: boolean;
};
type UserResponse = ApiResponse<{ name: string; age: number }>;
const res: UserResponse = { data: { name: "Bo", age: 28 }, isError: false };
// const bad: UserResponse = { data: { name: "Bo" }, isError: false }; // error: age missing
defaults and constraints: = and extends
Two advanced knobs, flagged as beyond the 90% use case. Defaults (type ApiResponse<TData = { status: number }>) let bare ApiResponse mean the common shape while still allowing overrides — most endpoints return { status: number } unless specified otherwise. Constraints (TData extends object) forbid whole categories: ApiResponse<string> errors because a string does not extend object; they compose with defaults (<TData extends object = { status: number }>). The closing calibration: 90% of real usage is one (maybe two) unconstrained parameters, usually in functions where inference supplies the argument for free.
type ApiResponse<TData extends object = { status: number }> = {
data: TData;
isError: boolean;
};
const ok: ApiResponse = { data: { status: 200 }, isError: false }; // default kicks in
// const bad: ApiResponse<string> = ...; // error: string does not extend object

the one-sentence rule
The takeaway, stated verbatim for reuse: when a function or type holds data that could be multiple different types across uses but must stay THE SAME type within a single use (a number array one call, a string array the next), use a generic. Paired course pointer: the presenter’s full TypeScript Simplified course (under 5 hours, linked in the description) extends this compressed style to every TypeScript concept.
Prev: 030 — 100+ JavaScript Concepts You Need to Know · Next: 032 — #04 Datatypes in TypeScript | TypeScript Basics & Data Types | A Complete TypeScript Course
extends constraints and default type arguments are explicitly beyond the 90% use case — introduce, do not drill. Transcript corrections: API response, document.querySelector, array .map, second type parameter U, { status: 200 }. Both captioned frames are talking-head; code evidence comes from the narrated editor sessions.