[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-weekly-typescript-generics":32,"code:ts:true:cevsgq":451,"code:ts:true:1balr4p":452,"code:ts:true:pirb76":453,"code:ts:true:b2nqhg":454,"code:ts:true:12unvlr":455,"code:ts:true:1k1392t":456,"code:ts:true:kavhl3":457,"code:ts:true:1mummyv":458,"code:ts:true:biy2ow":459,"code:ts:true:dgbube":460,"code:ts:true:urauza":461,"code:ts:true:1gutpa":462,"code:ts:true:10rt10m":463,"code:ts:true:k2hv74":464},[4,20],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Build. Learn. Ship.","Practical courses, engineering-grade articles and open-source tools for people who ship.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Structured paths, listening drills and spaced repetition that actually sticks.","amber-400","⌘",[13,15,29],"es",{"courses":17,"paths":17,"articles":18,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":17},2,{"id":33,"slug":34,"title":35,"subtitle":36,"excerpt":37,"coverUrl":38,"locale":13,"readingMinutes":39,"publishedAt":40,"viewCount":41,"likeCount":19,"commentCount":19,"author":42,"vertical":47,"topic":48,"tags":51,"_count":63,"playground":65,"body":67,"bodyMd":435,"seo":436,"translationGroupId":438,"thread":439,"assessments":441,"translations":447,"quiz":449},"019fe660-57eb-7258-b452-d66ff04d2380","weekly-typescript-generics","TypeScript Generics: The Complete Guide (with Cheat Sheet)",null,"Learn TypeScript generics from the ground up — generic functions, constraints, defaults, and classes — with worked examples and a copy-paste cheat sheet.","\u002Fmedia\u002Fcovers\u002Fweekly-typescript-generics.png",15,"2026-07-20T09:23:23.621Z",28,{"id":43,"name":44,"username":45,"avatarUrl":36,"headline":46},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":49,"name":50},"typescript","TypeScript",[52,54,57,60],{"slug":49,"name":53,"color":36},"Typescript",{"slug":55,"name":56,"color":36},"javascript","Javascript",{"slug":58,"name":59,"color":36},"tutorial","Tutorial",{"slug":61,"name":62,"color":36},"webdev","Webdev",{"assessments":64},1,{"slug":34,"title":66},"TypeScript Generics: any vs &lt;T&gt;",{"blocks":68,"version":64},[69,73,78,81,90,93,96,111,114,117,123,126,129,133,136,139,142,145,149,152,155,158,161,165,169,172,175,178,181,184,187,190,194,197,200,203,207,210,213,217,220,223,226,230,233,237,240,243,247,250,253,256,260,263,266,269,273,276,280,283,286,294,297,300,303,306,309,312,316,319,323,326,329,332,336,339,342,345,348,388,392,395,398,401,404,407,410,418,421,424,427],{"id":70,"html":71,"type":72},"b1","\u003Cp>You write a function that works on numbers. Then you need the same function for strings. You copy-paste it and change the type annotation. Then arrays. Then objects. Now you have four functions with identical bodies and different type signatures — or you gave up and typed everything \u003Ccode>any\u003C\u002Fcode>, and the compiler stopped helping you at the one place you needed it most: the boundary between what you pass in and what you get back.\u003C\u002Fp>","paragraph",{"id":74,"html":75,"text":76,"type":77,"level":31},"b2","What you&#39;ll learn","What you'll learn","heading",{"id":79,"html":80,"type":72},"b3","\u003Cp>By the end of this guide you&#39;ll be able to:\u003C\u002Fp>",{"id":82,"type":83,"items":84,"ordered":18},"b4","list",[85,86,87,88,89],"Write generic functions that preserve the exact type of their input in their output, instead of widening it to \u003Ccode>any\u003C\u002Fcode> or \u003Ccode>unknown\u003C\u002Fcode>","Constrain a type parameter with \u003Ccode>extends\u003C\u002Fcode> so the compiler enforces a shape without collapsing to one concrete type","Give a type parameter a sensible default, the way you&#39;d default a function argument","Read and write generic interfaces, types, and classes with more than one type parameter","Diagnose the most common generic inference failures instead of reaching for \u003Ccode>any\u003C\u002Fcode> to silence them",{"id":91,"html":92,"type":72},"b5","\u003Cp>\u003Cstrong>Who this is for:\u003C\u002Fstrong> you&#39;ve written TypeScript with interfaces and function signatures, and you&#39;ve seen \u003Ccode>&lt;T&gt;\u003C\u002Fcode> in library code, but you want the model that makes it predictable instead of memorized syntax.\u003C\u002Fp>",{"id":94,"html":95,"text":95,"type":77,"level":31},"b6","Contents",{"id":97,"type":83,"items":98,"ordered":18},"b7",[99,100,101,102,103,104,105,106,107,108,109,110],"\u003Ca href=\"#why-generics-exist\">Why generics exist\u003C\u002Fa>","\u003Ca href=\"#the-mental-model-a-type-parameter-is-a-function-argument-for-types\">The mental model\u003C\u002Fa>","\u003Ca href=\"#stage-1-your-first-generic-function\">Stage 1: your first generic function\u003C\u002Fa>","\u003Ca href=\"#stage-2-generic-interfaces-and-types\">Stage 2: generic interfaces and types\u003C\u002Fa>","\u003Ca href=\"#stage-3-constraining-a-type-parameter-with-extends\">Stage 3: constraining a type parameter with extends\u003C\u002Fa>","\u003Ca href=\"#stage-4-default-type-parameters\">Stage 4: default type parameters\u003C\u002Fa>","\u003Ca href=\"#stage-5-multiple-type-parameters-and-generic-classes\">Stage 5: multiple type parameters and generic classes\u003C\u002Fa>","\u003Ca href=\"#edge-cases-and-gotchas\">Edge cases and gotchas\u003C\u002Fa>","\u003Ca href=\"#best-practices-when-not-to-reach-for-generics\">Best practices\u003C\u002Fa>","\u003Ca href=\"#faq\">FAQ\u003C\u002Fa>","\u003Ca href=\"#cheat-sheet\">Cheat sheet\u003C\u002Fa>","\u003Ca href=\"#key-takeaways\">Key takeaways\u003C\u002Fa>",{"id":112,"html":113,"text":113,"type":77,"level":31},"b8","Why generics exist",{"id":115,"html":116,"type":72},"b9","\u003Cp>Here&#39;s the naive fix for &quot;I need this function to work on more than one type&quot; — widen the parameter to \u003Ccode>any\u003C\u002Fcode>:\u003C\u002Fp>",{"id":118,"code":119,"type":120,"language":121,"highlight":122},"b10","function firstElement(arr: any): any {\n  return arr[0];\n}\n\nconst n = firstElement([1, 2, 3]);      \u002F\u002F typed as `any` — no autocomplete, no error checking\nconst s = firstElement([\"a\", \"b\"]);     \u002F\u002F also `any` — TypeScript has no idea it's a string\nn.toUpperCase();                        \u002F\u002F compiles fine, crashes at runtime: not a string","code","ts",[],{"id":124,"html":125,"type":72},"b11","\u003Cp>The function works at runtime for both calls. But the moment you use the result, TypeScript has nothing to say — \u003Ccode>any\u003C\u002Fcode> erases the connection between what went in and what came out. You&#39;ve traded a compile-time error for a runtime crash, which is the one trade TypeScript exists to prevent.\u003C\u002Fp>",{"id":127,"html":128,"type":72},"b12","\u003Cp>The alternative most people try next is copy-pasting one function per type:\u003C\u002Fp>",{"id":130,"code":131,"type":120,"language":121,"highlight":132},"b13","function firstNumber(arr: number[]): number { return arr[0]; }\nfunction firstString(arr: string[]): string { return arr[0]; }\n\u002F\u002F ...one more function for every type you ever call this with",[],{"id":134,"html":135,"type":72},"b14","\u003Cp>This is type-safe, but it doesn&#39;t scale — you&#39;re maintaining N identical function bodies, and a bug fix means updating all of them. Generics solve exactly this: one function body, and a type that adapts to whatever you call it with, while the compiler still checks that the relationship holds.\u003C\u002Fp>",{"id":137,"html":138,"text":138,"type":77,"level":31},"b15","The mental model: a type parameter is a function argument for types",{"id":140,"html":141,"type":72},"b16","\u003Cp>\u003Cstrong>The mental model:\u003C\u002Fstrong> a generic \u003Ccode>&lt;T&gt;\u003C\u002Fcode> is a parameter, exactly like a function parameter — except instead of a value, you&#39;re passing in a \u003Cem>type\u003C\u002Fem>, and the compiler substitutes it everywhere \u003Ccode>T\u003C\u002Fcode> appears and checks that everything stays consistent.\u003C\u002Fp>",{"id":143,"html":144,"type":72},"b17","\u003Cp>Compare the two side by side:\u003C\u002Fp>",{"id":146,"code":147,"type":120,"language":121,"highlight":148},"b18","\u002F\u002F a regular function: `x` is a placeholder for a VALUE, filled in at the call site\nfunction double(x: number): number {\n  return x * 2;\n}\ndouble(5); \u002F\u002F x = 5\n\n\u002F\u002F a generic function: `T` is a placeholder for a TYPE, filled in at the call site\nfunction identity\u003CT>(x: T): T {\n  return x;\n}\nidentity(5);      \u002F\u002F T = number\nidentity(\"hi\");   \u002F\u002F T = string",[],{"id":150,"html":151,"type":72},"b19","\u003Cp>\u003Ccode>double\u003C\u002Fcode>&#39;s parameter list says &quot;give me a number, I&#39;ll give you a number.&quot; \u003Ccode>identity\u003C\u002Fcode>&#39;s type parameter list says &quot;give me \u003Cem>some\u003C\u002Fem> type T, and I promise to give you back that same T&quot; — it&#39;s a contract about the \u003Cem>shape of the relationship\u003C\u002Fem>, not about one fixed type. TypeScript infers \u003Ccode>T\u003C\u002Fcode> from the argument you actually pass, the same way it infers the type of a variable from its initializer — you rarely write \u003Ccode>identity&lt;number&gt;(5)\u003C\u002Fcode> explicitly; the compiler figures out \u003Ccode>T = number\u003C\u002Fcode> on its own.\u003C\u002Fp>",{"id":153,"html":154,"type":72},"b20","\u003Cp>That&#39;s the whole idea. Everything below is this one substitution rule, applied to interfaces, constraints, defaults, and classes.\u003C\u002Fp>",{"id":156,"html":157,"text":157,"type":77,"level":31},"b21","Stage 1: your first generic function",{"id":159,"html":160,"type":72},"b22","\u003Cp>Fix the \u003Ccode>firstElement\u003C\u002Fcode> function from earlier with a type parameter instead of \u003Ccode>any\u003C\u002Fcode>:\u003C\u002Fp>",{"id":162,"code":163,"type":120,"language":121,"highlight":164},"b23","function firstElement\u003CT>(arr: T[]): T | undefined {\n  return arr[0];\n}\n\nconst n = firstElement([1, 2, 3]);    \u002F\u002F n: number | undefined\nconst s = firstElement([\"a\", \"b\"]);   \u002F\u002F s: string | undefined\nn?.toFixed(2);                        \u002F\u002F ✅ compiler knows n might be a number\ns?.toUpperCase();                     \u002F\u002F ✅ compiler knows s might be a string",[],{"id":166,"html":167,"type":168},"b24","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> \u003Ccode>T\u003C\u002Fcode> is inferred once, from the argument, and then reused for the return type. The function body never mentions \u003Ccode>number\u003C\u002Fcode> or \u003Ccode>string\u003C\u002Fcode> — it stays generic, but every call site gets a fully concrete, checked type back.\u003C\u002Fp>\n","quote",{"id":170,"html":171,"type":72},"b25","\u003C!-- playground:start -->",{"id":173,"html":174,"text":174,"type":77,"level":31},"b26","🎮 Try it yourself",{"id":176,"html":177,"type":72},"b27","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fposts\u002F2026-07-20-weekly-typescript-generics\u002Fplayground\u002F\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":179,"html":180,"type":72},"b28","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":182,"html":183,"type":72},"b29","\u003C!-- playground:end -->",{"id":185,"html":186,"type":72},"b30","\u003Cp>The \u003Ccode>T | undefined\u003C\u002Fcode> return type matters too: an empty array is a valid \u003Ccode>T[]\u003C\u002Fcode>, so the function is honest that there might be nothing at index 0. This is a case where \u003Ccode>any\u003C\u002Fcode> would have silently hidden a real edge case that \u003Ccode>T | undefined\u003C\u002Fcode> forces you to handle.\u003C\u002Fp>",{"id":188,"html":189,"type":72},"b31","\u003Cp>Generic functions can also take more than the type parameter implies. A \u003Ccode>map\u003C\u002Fcode>-style helper needs a second, ordinary parameter — a callback — that also mentions \u003Ccode>T\u003C\u002Fcode>:\u003C\u002Fp>",{"id":191,"code":192,"type":120,"language":121,"highlight":193},"b32","function mapArray\u003CT, U>(arr: T[], fn: (item: T) => U): U[] {\n  return arr.map(fn);\n}\n\nconst lengths = mapArray([\"a\", \"bb\", \"ccc\"], (s) => s.length); \u002F\u002F lengths: number[]",[],{"id":195,"html":196,"type":72},"b33","\u003Cp>Here \u003Ccode>T\u003C\u002Fcode> is inferred from \u003Ccode>arr\u003C\u002Fcode> (\u003Ccode>string\u003C\u002Fcode>) and \u003Ccode>U\u003C\u002Fcode> is inferred from what \u003Ccode>fn\u003C\u002Fcode> returns (\u003Ccode>number\u003C\u002Fcode>) — two independent type parameters, each pinned down by a different argument.\u003C\u002Fp>",{"id":198,"html":199,"text":199,"type":77,"level":31},"b34","Stage 2: generic interfaces and types",{"id":201,"html":202,"type":72},"b35","\u003Cp>The same substitution rule applies to \u003Ccode>interface\u003C\u002Fcode> and \u003Ccode>type\u003C\u002Fcode>, not just functions. A generic interface is a shape with a type-level blank to fill in:\u003C\u002Fp>",{"id":204,"code":205,"type":120,"language":121,"highlight":206},"b36","interface Box\u003CT> {\n  value: T;\n}\n\nconst numberBox: Box\u003Cnumber> = { value: 42 };\nconst stringBox: Box\u003Cstring> = { value: \"hello\" };\n\u002F\u002F const bad: Box\u003Cnumber> = { value: \"hello\" }; \u002F\u002F ❌ string is not assignable to number",[],{"id":208,"html":209,"type":72},"b37","\u003Cp>\u003Ccode>Box&lt;T&gt;\u003C\u002Fcode> isn&#39;t a type by itself — it&#39;s a \u003Cem>template\u003C\u002Fem> for a type. \u003Ccode>Box&lt;number&gt;\u003C\u002Fcode> and \u003Ccode>Box&lt;string&gt;\u003C\u002Fcode> are the actual types, produced by substituting \u003Ccode>T\u003C\u002Fcode>. This is the exact same mechanism as \u003Ccode>Array&lt;T&gt;\u003C\u002Fcode> (the type behind \u003Ccode>T[]\u003C\u002Fcode>), \u003Ccode>Promise&lt;T&gt;\u003C\u002Fcode>, and \u003Ccode>Map&lt;K, V&gt;\u003C\u002Fcode> from the standard library — you&#39;ve been using generics since your first \u003Ccode>Promise&lt;void&gt;\u003C\u002Fcode>, you just hadn&#39;t named the mechanism yet.\u003C\u002Fp>",{"id":211,"html":212,"type":72},"b38","\u003Cp>Generic type aliases work identically and are common for API response shapes:\u003C\u002Fp>",{"id":214,"code":215,"type":120,"language":121,"highlight":216},"b39","type ApiResponse\u003CT> = {\n  data: T;\n  error: string | null;\n};\n\nasync function getUser(id: string): Promise\u003CApiResponse\u003CUser>> {\n  \u002F\u002F ...\n}",[],{"id":218,"html":219,"type":72},"b40","\u003Cp>Every endpoint reuses one \u003Ccode>ApiResponse&lt;T&gt;\u003C\u002Fcode> shape instead of a hand-written \u003Ccode>UserResponse\u003C\u002Fcode>, \u003Ccode>PostResponse\u003C\u002Fcode>, \u003Ccode>OrderResponse\u003C\u002Fcode> trio that all say the same thing with different names.\u003C\u002Fp>",{"id":221,"html":222,"text":222,"type":77,"level":31},"b41","Stage 3: constraining a type parameter with extends",{"id":224,"html":225,"type":72},"b42","\u003Cp>Unconstrained, \u003Ccode>T\u003C\u002Fcode> could be anything — which means you can&#39;t assume it has any properties at all:\u003C\u002Fp>",{"id":227,"code":228,"type":120,"language":121,"highlight":229},"b43","function getLength\u003CT>(item: T): number {\n  return item.length; \u002F\u002F ❌ Property 'length' does not exist on type 'T'\n}",[],{"id":231,"html":232,"type":72},"b44","\u003Cp>The compiler is right to reject this: nothing says \u003Ccode>T\u003C\u002Fcode> has a \u003Ccode>.length\u003C\u002Fcode>. The fix is a \u003Cstrong>constraint\u003C\u002Fstrong> — \u003Ccode>extends\u003C\u002Fcode> here doesn&#39;t mean &quot;inherits from,&quot; it means &quot;must be assignable to&quot;:\u003C\u002Fp>",{"id":234,"code":235,"type":120,"language":121,"highlight":236},"b45","interface HasLength {\n  length: number;\n}\n\nfunction getLength\u003CT extends HasLength>(item: T): number {\n  return item.length; \u002F\u002F ✅ every T that reaches this line is guaranteed to have `.length`\n}\n\ngetLength(\"hello\");        \u002F\u002F ✅ strings have .length\ngetLength([1, 2, 3]);      \u002F\u002F ✅ arrays have .length\ngetLength({ length: 10 }); \u002F\u002F ✅ any object shape with a length field\ngetLength(42);              \u002F\u002F ❌ number has no .length",[],{"id":238,"html":239,"type":168},"b46","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> \u003Ccode>T extends HasLength\u003C\u002Fcode> narrows \u003Cem>which types are legal\u003C\u002Fem>, not what \u003Ccode>T\u003C\u002Fcode> collapses to. \u003Ccode>T\u003C\u002Fcode> is still inferred per call — a \u003Ccode>string\u003C\u002Fcode>, an array, or a custom object — the constraint only guarantees the one property you need.\u003C\u002Fp>\n",{"id":241,"html":242,"type":72},"b47","\u003Cp>A very common constraint pattern uses \u003Ccode>keyof\u003C\u002Fcode> to guarantee a key actually exists on an object, which is what makes a type-safe \u003Ccode>getProperty\u003C\u002Fcode> helper possible:\u003C\u002Fp>",{"id":244,"code":245,"type":120,"language":121,"highlight":246},"b48","function getProperty\u003CT, K extends keyof T>(obj: T, key: K): T[K] {\n  return obj[key];\n}\n\nconst user = { name: \"Ada\", age: 36 };\ngetProperty(user, \"name\");   \u002F\u002F ✅ inferred as string\ngetProperty(user, \"email\");  \u002F\u002F ❌ \"email\" is not a key of { name: string; age: number }",[],{"id":248,"html":249,"type":72},"b49","\u003Cp>\u003Ccode>K extends keyof T\u003C\u002Fcode> reads as &quot;K must be one of T&#39;s actual property names.&quot; Once that holds, \u003Ccode>T[K]\u003C\u002Fcode> — an \u003Cem>indexed access type\u003C\u002Fem> — gives you the precise type of that property, so \u003Ccode>getProperty(user, &quot;age&quot;)\u003C\u002Fcode> returns \u003Ccode>number\u003C\u002Fcode>, not some generic \u003Ccode>unknown\u003C\u002Fcode>.\u003C\u002Fp>",{"id":251,"html":252,"text":252,"type":77,"level":31},"b50","Stage 4: default type parameters",{"id":254,"html":255,"type":72},"b51","\u003Cp>A type parameter can have a default, exactly like a function parameter can have a default value — used when the caller doesn&#39;t supply one:\u003C\u002Fp>",{"id":257,"code":258,"type":120,"language":121,"highlight":259},"b52","interface FetchOptions\u003CTResponse = unknown> {\n  url: string;\n  parse?: (raw: string) => TResponse;\n}\n\nconst raw: FetchOptions = { url: \"\u002Fping\" };                 \u002F\u002F TResponse defaults to unknown\nconst typed: FetchOptions\u003C{ ok: boolean }> = {               \u002F\u002F explicit, overrides the default\n  url: \"\u002Fhealth\",\n  parse: (raw) => JSON.parse(raw),\n};",[],{"id":261,"html":262,"type":72},"b53","\u003Cp>Defaults matter most on widely-used generic types where most callers don&#39;t care about every parameter — a state-management or component-library type might have four type parameters with sensible defaults so 90% of call sites only ever write the one that matters to them, and the rest fall back silently.\u003C\u002Fp>",{"id":264,"html":265,"text":265,"type":77,"level":31},"b54","Stage 5: multiple type parameters and generic classes",{"id":267,"html":268,"type":72},"b55","\u003Cp>Nothing restricts you to one \u003Ccode>T\u003C\u002Fcode>. Multiple type parameters are common wherever there&#39;s a key\u002Fvalue or input\u002Foutput relationship:\u003C\u002Fp>",{"id":270,"code":271,"type":120,"language":121,"highlight":272},"b56","function zip\u003CA, B>(a: A[], b: B[]): [A, B][] {\n  return a.map((item, i) => [item, b[i]]);\n}\n\nzip([1, 2], [\"a\", \"b\"]); \u002F\u002F [number, string][] — inferred as [[1, \"a\"], [2, \"b\"]]",[],{"id":274,"html":275,"type":72},"b57","\u003Cp>Classes take type parameters the same way functions do — on the class itself, available to every method and property inside:\u003C\u002Fp>",{"id":277,"code":278,"type":120,"language":121,"highlight":279},"b58","class Stack\u003CT> {\n  private items: T[] = [];\n\n  push(item: T): void {\n    this.items.push(item);\n  }\n\n  pop(): T | undefined {\n    return this.items.pop();\n  }\n\n  get size(): number {\n    return this.items.length;\n  }\n}\n\nconst numbers = new Stack\u003Cnumber>();\nnumbers.push(1);\nnumbers.push(2);\nnumbers.pop(); \u002F\u002F number | undefined\n\nconst strings = new Stack\u003Cstring>();\nstrings.push(\"a\");\n\u002F\u002F strings.push(1); \u002F\u002F ❌ number is not assignable to string",[],{"id":281,"html":282,"type":72},"b59","\u003Cp>\u003Ccode>T\u003C\u002Fcode> is fixed once, at \u003Ccode>new Stack&lt;number&gt;()\u003C\u002Fcode>, and every method on that instance uses that same \u003Ccode>T\u003C\u002Fcode> for the rest of its life — \u003Ccode>push\u003C\u002Fcode> and \u003Ccode>pop\u003C\u002Fcode> stay in sync automatically, without repeating the type anywhere else in the class body.\u003C\u002Fp>",{"id":284,"html":285,"text":285,"type":77,"level":31},"b60","Edge cases and gotchas",{"id":287,"type":83,"items":288,"ordered":18},"b61",[289,290,291,292,293],"\u003Cstrong>Type erasure at runtime.\u003C\u002Fstrong> Generics are a compile-time-only construct — they&#39;re erased entirely from the emitted JavaScript. You cannot check \u003Ccode>typeof T\u003C\u002Fcode> or branch on \u003Ccode>T\u003C\u002Fcode> at runtime; if you need runtime knowledge of the type, you must pass it explicitly as a value (a constructor, a discriminant field, or a plain string tag), because by the time your code runs, \u003Ccode>T\u003C\u002Fcode> no longer exists.","\u003Cstrong>Inference can fail on object literals with contextual typing.\u003C\u002Fstrong> \u003Ccode>identity({ name: &quot;Ada&quot; })\u003C\u002Fcode> infers \u003Ccode>T\u003C\u002Fcode> as \u003Ccode>{ name: string }\u003C\u002Fcode>, which is often what you want, but if you need a \u003Cem>wider\u003C\u002Fem> inferred literal type (e.g. keeping a string as a specific literal rather than widening to \u003Ccode>string\u003C\u002Fcode>), you&#39;ll need \u003Ccode>as const\u003C\u002Fcode> on the argument, not a change to the generic itself.","\u003Cstrong>Arrow functions with generics need care in \u003Ccode>.tsx\u003C\u002Fcode> files.\u003C\u002Fstrong> \u003Ccode>const identity = &lt;T&gt;(x: T) =&gt; x;\u003C\u002Fcode> can be parsed as a JSX opening tag inside a \u003Ccode>.tsx\u003C\u002Fcode> file. The common workaround is a trailing comma — \u003Ccode>&lt;T,&gt;(x: T) =&gt; x\u003C\u002Fcode> — or naming a constraint explicitly, \u003Ccode>&lt;T extends unknown&gt;(x: T) =&gt; x\u003C\u002Fcode>.","\u003Cstrong>Conditional types distribute over unions unless you stop them.\u003C\u002Fstrong> \u003Ccode>type ToArray&lt;T&gt; = T extends any ? T[] : never;\u003C\u002Fcode> applied to \u003Ccode>string | number\u003C\u002Fcode> produces \u003Ccode>string[] | number[]\u003C\u002Fcode>, not \u003Ccode>(string | number)[]\u003C\u002Fcode>, because a naked type parameter in a conditional type distributes across each union member individually. Wrapping both sides in a tuple (\u003Ccode>[T] extends [any] ? ...\u003C\u002Fcode>) disables that distribution when you don&#39;t want it.","\u003Cstrong>A constraint is a floor, not the resolved type.\u003C\u002Fstrong> \u003Ccode>function f&lt;T extends { id: string }&gt;(x: T)\u003C\u002Fcode> still infers the caller&#39;s \u003Cem>full\u003C\u002Fem> type for \u003Ccode>T\u003C\u002Fcode> (every extra property survives), not just \u003Ccode>{ id: string }\u003C\u002Fcode> — the constraint only restricts which types are legal, it doesn&#39;t narrow what gets stored in \u003Ccode>T\u003C\u002Fcode>.",{"id":295,"html":296,"text":296,"type":77,"level":31},"b62","Best practices: when (not) to reach for generics",{"id":298,"html":299,"type":72},"b63","\u003Cp>\u003Cstrong>Reach for a generic when\u003C\u002Fstrong> a function or type&#39;s inputs and outputs are \u003Cem>related\u003C\u002Fem> and that relationship needs to survive into the caller&#39;s code — a \u003Ccode>firstElement&lt;T&gt;\u003C\u002Fcode> that returns the same \u003Ccode>T\u003C\u002Fcode> it received, a \u003Ccode>Box&lt;T&gt;\u003C\u002Fcode> that wraps any payload, an \u003Ccode>ApiResponse&lt;T&gt;\u003C\u002Fcode> reused across every endpoint.\u003C\u002Fp>",{"id":301,"html":302,"type":72},"b64","\u003Cp>\u003Cstrong>Don&#39;t reach for a generic when\u003C\u002Fstrong> a union type says what you mean more simply. If a parameter only ever needs to be \u003Ccode>&quot;light&quot; | &quot;dark&quot;\u003C\u002Fcode>, write that union — a \u003Ccode>&lt;T extends &quot;light&quot; | &quot;dark&quot;&gt;\u003C\u002Fcode> generic adds ceremony without adding safety.\u003C\u002Fp>",{"id":304,"html":305,"type":72},"b65","\u003Cp>\u003Cstrong>Don&#39;t reach for a generic to avoid writing \u003Ccode>unknown\u003C\u002Fcode>.\u003C\u002Fstrong> If a value&#39;s type genuinely isn&#39;t knowable until you inspect it at runtime (parsed JSON, a \u003Ccode>catch\u003C\u002Fcode> block&#39;s error), the honest type is \u003Ccode>unknown\u003C\u002Fcode> plus a runtime check — not a type parameter with no way to constrain it, and not \u003Ccode>any\u003C\u002Fcode>, which is how you end up back at the \u003Ccode>firstElement\u003C\u002Fcode> bug from the top of this article. TypeScript&#39;s own \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fyour-typescript-config-objects-are-losing-type-information-satisfies-fixes-it-3hdh\">\u003Ccode>satisfies\u003C\u002Fcode> operator\u003C\u002Fa> solves a related but different problem — keeping a literal&#39;s narrow type while still validating it against a shape — worth knowing alongside generics, not instead of them.\u003C\u002Fp>",{"id":307,"html":308,"type":72},"b66","\u003Cp>\u003Cstrong>Don&#39;t over-parameterize.\u003C\u002Fstrong> A function with four type parameters, three of them defaulted and never varied at any call site in your codebase, is a sign the abstraction arrived before the second real use case did. Write the concrete version first; generalize when a second caller actually needs a different type.\u003C\u002Fp>",{"id":310,"html":311,"text":311,"type":77,"level":31},"b67","FAQ",{"id":313,"html":314,"text":314,"type":77,"level":315},"b68","What is a generic in TypeScript?",3,{"id":317,"html":318,"type":72},"b69","\u003Cp>A generic is a type parameter — a placeholder for a type, written in angle brackets like \u003Ccode>&lt;T&gt;\u003C\u002Fcode> — that lets a function, interface, type, or class work with any type while the compiler still tracks and checks that type through every use, instead of collapsing everything to \u003Ccode>any\u003C\u002Fcode>.\u003C\u002Fp>",{"id":320,"html":321,"text":322,"type":77,"level":315},"b70","Is \u003Ccode>T extends SomeType\u003C\u002Fcode> the same as class inheritance?","Is T extends SomeType the same as class inheritance?",{"id":324,"html":325,"type":72},"b71","\u003Cp>No. In a generic constraint, \u003Ccode>extends\u003C\u002Fcode> means &quot;must be assignable to&quot; or &quot;must have at least this shape&quot; — it restricts which types are legal for \u003Ccode>T\u003C\u002Fcode>, not a class hierarchy. \u003Ccode>T extends { id: string }\u003C\u002Fcode> accepts any type with an \u003Ccode>id: string\u003C\u002Fcode> property, not just subclasses of some \u003Ccode>{ id: string }\u003C\u002Fcode> class.\u003C\u002Fp>",{"id":327,"html":328,"text":328,"type":77,"level":315},"b72","Can I use a default value for a generic type parameter?",{"id":330,"html":331,"type":72},"b73","\u003Cp>Yes — \u003Ccode>interface Box&lt;T = unknown&gt;\u003C\u002Fcode> gives \u003Ccode>T\u003C\u002Fcode> a default the same way a function parameter can have one. Callers that don&#39;t specify a type argument get the default; callers that do (\u003Ccode>Box&lt;number&gt;\u003C\u002Fcode>) override it.\u003C\u002Fp>",{"id":333,"html":334,"text":335,"type":77,"level":315},"b74","Is \u003Ccode>any\u003C\u002Fcode> the same as a generic?","Is any the same as a generic?",{"id":337,"html":338,"type":72},"b75","\u003Cp>No, and this is the most common confusion. \u003Ccode>any\u003C\u002Fcode> disables type checking entirely for that value — TypeScript stops tracking it. A generic \u003Ccode>T\u003C\u002Fcode> keeps type checking fully active; it just defers \u003Cem>which\u003C\u002Fem> type until the call site, and enforces that the same \u003Ccode>T\u003C\u002Fcode> is used consistently everywhere it appears.\u003C\u002Fp>",{"id":340,"html":341,"text":341,"type":77,"level":315},"b76","Do generic types exist at runtime?",{"id":343,"html":344,"type":72},"b77","\u003Cp>No. TypeScript&#39;s type system, including all generics, is erased during compilation — there is no \u003Ccode>T\u003C\u002Fcode> in the emitted JavaScript, and you cannot inspect it with \u003Ccode>typeof\u003C\u002Fcode> or \u003Ccode>instanceof\u003C\u002Fcode> at runtime. Anything you need to know while the program is running must be represented as an actual runtime value, not just a type parameter.\u003C\u002Fp>",{"id":346,"html":347,"text":347,"type":77,"level":31},"b78","Cheat sheet",{"id":349,"head":350,"rows":354,"type":387},"b79",[351,352,353],"Task","Syntax","Notes",[355,359,363,367,371,375,379,383],[356,357,358],"Generic function","\u003Ccode>function f&lt;T&gt;(x: T): T\u003C\u002Fcode>","\u003Ccode>T\u003C\u002Fcode> inferred from the argument",[360,361,362],"Generic interface\u002Ftype","\u003Ccode>interface Box&lt;T&gt; { value: T }\u003C\u002Fcode>","Instantiate as \u003Ccode>Box&lt;number&gt;\u003C\u002Fcode>",[364,365,366],"Multiple type parameters","\u003Ccode>function zip&lt;A, B&gt;(a: A[], b: B[])\u003C\u002Fcode>","Each inferred independently",[368,369,370],"Constrain a type parameter","\u003Ccode>&lt;T extends HasLength&gt;\u003C\u002Fcode>","&quot;Must be assignable to,&quot; not inheritance",[372,373,374],"Require a real key of an object","\u003Ccode>&lt;T, K extends keyof T&gt;\u003C\u002Fcode>","Pairs with indexed access: \u003Ccode>T[K]\u003C\u002Fcode>",[376,377,378],"Default type parameter","\u003Ccode>&lt;T = unknown&gt;\u003C\u002Fcode>","Used when the caller omits a type argument",[380,381,382],"Generic class","\u003Ccode>class Stack&lt;T&gt; { push(item: T): void }\u003C\u002Fcode>","\u003Ccode>T\u003C\u002Fcode> fixed once, at \u003Ccode>new Stack&lt;number&gt;()\u003C\u002Fcode>",[384,385,386],"Explicit type argument (rare)","\u003Ccode>identity&lt;string&gt;(&quot;hi&quot;)\u003C\u002Fcode>","Usually unnecessary — inference handles it","table",{"id":389,"code":390,"type":120,"language":121,"highlight":391},"b80","\u002F\u002F The whole pattern, copy-paste ready\ninterface ApiResponse\u003CTData = unknown> {\n  data: TData;\n  error: string | null;\n}\n\nfunction firstMatching\u003CT, K extends keyof T>(\n  items: T[],\n  key: K,\n  value: T[K]\n): T | undefined {\n  return items.find((item) => item[key] === value);\n}\n\nclass Cache\u003CT> {\n  private store = new Map\u003Cstring, T>();\n  set(key: string, value: T): void { this.store.set(key, value); }\n  get(key: string): T | undefined { return this.store.get(key); }\n}\n\n\u002F\u002F usage — every T flows through, checked, with no `any` anywhere\nconst cache = new Cache\u003CApiResponse\u003C{ id: string }>>();",[],{"id":393,"html":394,"type":72},"b81","\u003C!-- quiz:start -->",{"id":396,"html":397,"text":397,"type":77,"level":31},"b82","🧠 Test yourself",{"id":399,"html":400,"type":72},"b83","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fquiz\u002Ftake.html?post=2026-07-20-weekly-typescript-generics\">Take the 7-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":402,"html":403,"type":72},"b84","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":405,"html":406,"type":72},"b85","\u003C!-- quiz:end -->",{"id":408,"html":409,"text":409,"type":77,"level":31},"b86","Key takeaways",{"id":411,"type":83,"items":412,"ordered":18},"b87",[413,414,415,416,417],"A type parameter is a function argument for types — the compiler substitutes it at the call site and checks the relationship stays consistent, the same way it checks a value argument.","\u003Ccode>extends\u003C\u002Fcode> in a generic constraint means &quot;must be assignable to,&quot; not class inheritance — it narrows which types are legal without collapsing \u003Ccode>T\u003C\u002Fcode> to one fixed type.","Reach for a generic when an input and an output (or two inputs) are \u003Cem>related\u003C\u002Fem> and that relationship must survive into the caller&#39;s code; reach for a plain union or \u003Ccode>unknown\u003C\u002Fcode> when it doesn&#39;t.","Generics are erased at compile time — there&#39;s no \u003Ccode>T\u003C\u002Fcode> left at runtime, so runtime type decisions need an actual value, not a type parameter.","\u003Ccode>any\u003C\u002Fcode> and a generic solve opposite problems: \u003Ccode>any\u003C\u002Fcode> turns off checking, a generic keeps it on while deferring which type.",{"id":419,"html":420,"type":72},"b88","\u003Cp>You started this article maintaining four near-identical functions, one per type, or one function typed \u003Ccode>any\u003C\u002Fcode> that quietly let a string crash on \u003Ccode>.toUpperCase()\u003C\u002Fcode>. Now you have the one mechanism — a type parameter, substituted and checked at every call site — that replaces both: a single \u003Ccode>firstElement&lt;T&gt;\u003C\u002Fcode>, a single \u003Ccode>Cache&lt;T&gt;\u003C\u002Fcode>, a single \u003Ccode>ApiResponse&lt;T&gt;\u003C\u002Fcode>, each one exactly as safe as if you&#39;d hand-written it for every type you&#39;ll ever call it with. Where&#39;s the first place in your own codebase you&#39;ll delete a copy-pasted function and replace it with one generic? Tell me in the comments.\u003C\u002Fp>",{"id":422,"type":423},"b89","divider",{"id":425,"html":426,"type":72},"b90","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":428,"type":83,"items":429,"ordered":18},"b91",[430,431,432,433,434],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","💼 \u003Cstrong>LinkedIn\u003C\u002Fstrong> — \u003Ca href=\"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F\">linkedin.com\u002Fin\u002Fparsa-jiravand\u003C\u002Fa>","✉️ \u003Cstrong>Email\u003C\u002Fstrong> (work &amp; contract inquiries): \u003Ca href=\"mailto:bestpractice2026@gmail.com\">bestpractice2026@gmail.com\u003C\u002Fa>","You write a function that works on numbers. Then you need the same function for strings. You copy-paste it and change the type annotation. Then arrays. Then objects. Now you have four functions with identical bodies and different type signatures — or you gave up and typed everything `any`, and the compiler stopped helping you at the one place you needed it most: the boundary between what you pass in and what you get back.\n\n## What you'll learn\n\nBy the end of this guide you'll be able to:\n\n- Write generic functions that preserve the exact type of their input in their output, instead of widening it to `any` or `unknown`\n- Constrain a type parameter with `extends` so the compiler enforces a shape without collapsing to one concrete type\n- Give a type parameter a sensible default, the way you'd default a function argument\n- Read and write generic interfaces, types, and classes with more than one type parameter\n- Diagnose the most common generic inference failures instead of reaching for `any` to silence them\n\n**Who this is for:** you've written TypeScript with interfaces and function signatures, and you've seen `\u003CT>` in library code, but you want the model that makes it predictable instead of memorized syntax.\n\n## Contents\n\n- [Why generics exist](#why-generics-exist)\n- [The mental model](#the-mental-model-a-type-parameter-is-a-function-argument-for-types)\n- [Stage 1: your first generic function](#stage-1-your-first-generic-function)\n- [Stage 2: generic interfaces and types](#stage-2-generic-interfaces-and-types)\n- [Stage 3: constraining a type parameter with extends](#stage-3-constraining-a-type-parameter-with-extends)\n- [Stage 4: default type parameters](#stage-4-default-type-parameters)\n- [Stage 5: multiple type parameters and generic classes](#stage-5-multiple-type-parameters-and-generic-classes)\n- [Edge cases and gotchas](#edge-cases-and-gotchas)\n- [Best practices](#best-practices-when-not-to-reach-for-generics)\n- [FAQ](#faq)\n- [Cheat sheet](#cheat-sheet)\n- [Key takeaways](#key-takeaways)\n\n## Why generics exist\n\nHere's the naive fix for \"I need this function to work on more than one type\" — widen the parameter to `any`:\n\n```ts\nfunction firstElement(arr: any): any {\n  return arr[0];\n}\n\nconst n = firstElement([1, 2, 3]);      \u002F\u002F typed as `any` — no autocomplete, no error checking\nconst s = firstElement([\"a\", \"b\"]);     \u002F\u002F also `any` — TypeScript has no idea it's a string\nn.toUpperCase();                        \u002F\u002F compiles fine, crashes at runtime: not a string\n```\n\nThe function works at runtime for both calls. But the moment you use the result, TypeScript has nothing to say — `any` erases the connection between what went in and what came out. You've traded a compile-time error for a runtime crash, which is the one trade TypeScript exists to prevent.\n\nThe alternative most people try next is copy-pasting one function per type:\n\n```ts\nfunction firstNumber(arr: number[]): number { return arr[0]; }\nfunction firstString(arr: string[]): string { return arr[0]; }\n\u002F\u002F ...one more function for every type you ever call this with\n```\n\nThis is type-safe, but it doesn't scale — you're maintaining N identical function bodies, and a bug fix means updating all of them. Generics solve exactly this: one function body, and a type that adapts to whatever you call it with, while the compiler still checks that the relationship holds.\n\n## The mental model: a type parameter is a function argument for types\n\n**The mental model:** a generic `\u003CT>` is a parameter, exactly like a function parameter — except instead of a value, you're passing in a *type*, and the compiler substitutes it everywhere `T` appears and checks that everything stays consistent.\n\nCompare the two side by side:\n\n```ts\n\u002F\u002F a regular function: `x` is a placeholder for a VALUE, filled in at the call site\nfunction double(x: number): number {\n  return x * 2;\n}\ndouble(5); \u002F\u002F x = 5\n\n\u002F\u002F a generic function: `T` is a placeholder for a TYPE, filled in at the call site\nfunction identity\u003CT>(x: T): T {\n  return x;\n}\nidentity(5);      \u002F\u002F T = number\nidentity(\"hi\");   \u002F\u002F T = string\n```\n\n`double`'s parameter list says \"give me a number, I'll give you a number.\" `identity`'s type parameter list says \"give me *some* type T, and I promise to give you back that same T\" — it's a contract about the *shape of the relationship*, not about one fixed type. TypeScript infers `T` from the argument you actually pass, the same way it infers the type of a variable from its initializer — you rarely write `identity\u003Cnumber>(5)` explicitly; the compiler figures out `T = number` on its own.\n\nThat's the whole idea. Everything below is this one substitution rule, applied to interfaces, constraints, defaults, and classes.\n\n## Stage 1: your first generic function\n\nFix the `firstElement` function from earlier with a type parameter instead of `any`:\n\n```ts\nfunction firstElement\u003CT>(arr: T[]): T | undefined {\n  return arr[0];\n}\n\nconst n = firstElement([1, 2, 3]);    \u002F\u002F n: number | undefined\nconst s = firstElement([\"a\", \"b\"]);   \u002F\u002F s: string | undefined\nn?.toFixed(2);                        \u002F\u002F ✅ compiler knows n might be a number\ns?.toUpperCase();                     \u002F\u002F ✅ compiler knows s might be a string\n```\n\n> **Key concept:** `T` is inferred once, from the argument, and then reused for the return type. The function body never mentions `number` or `string` — it stays generic, but every call site gets a fully concrete, checked type back.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fposts\u002F2026-07-20-weekly-typescript-generics\u002Fplayground\u002F)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\nThe `T | undefined` return type matters too: an empty array is a valid `T[]`, so the function is honest that there might be nothing at index 0. This is a case where `any` would have silently hidden a real edge case that `T | undefined` forces you to handle.\n\nGeneric functions can also take more than the type parameter implies. A `map`-style helper needs a second, ordinary parameter — a callback — that also mentions `T`:\n\n```ts\nfunction mapArray\u003CT, U>(arr: T[], fn: (item: T) => U): U[] {\n  return arr.map(fn);\n}\n\nconst lengths = mapArray([\"a\", \"bb\", \"ccc\"], (s) => s.length); \u002F\u002F lengths: number[]\n```\n\nHere `T` is inferred from `arr` (`string`) and `U` is inferred from what `fn` returns (`number`) — two independent type parameters, each pinned down by a different argument.\n\n## Stage 2: generic interfaces and types\n\nThe same substitution rule applies to `interface` and `type`, not just functions. A generic interface is a shape with a type-level blank to fill in:\n\n```ts\ninterface Box\u003CT> {\n  value: T;\n}\n\nconst numberBox: Box\u003Cnumber> = { value: 42 };\nconst stringBox: Box\u003Cstring> = { value: \"hello\" };\n\u002F\u002F const bad: Box\u003Cnumber> = { value: \"hello\" }; \u002F\u002F ❌ string is not assignable to number\n```\n\n`Box\u003CT>` isn't a type by itself — it's a *template* for a type. `Box\u003Cnumber>` and `Box\u003Cstring>` are the actual types, produced by substituting `T`. This is the exact same mechanism as `Array\u003CT>` (the type behind `T[]`), `Promise\u003CT>`, and `Map\u003CK, V>` from the standard library — you've been using generics since your first `Promise\u003Cvoid>`, you just hadn't named the mechanism yet.\n\nGeneric type aliases work identically and are common for API response shapes:\n\n```ts\ntype ApiResponse\u003CT> = {\n  data: T;\n  error: string | null;\n};\n\nasync function getUser(id: string): Promise\u003CApiResponse\u003CUser>> {\n  \u002F\u002F ...\n}\n```\n\nEvery endpoint reuses one `ApiResponse\u003CT>` shape instead of a hand-written `UserResponse`, `PostResponse`, `OrderResponse` trio that all say the same thing with different names.\n\n## Stage 3: constraining a type parameter with extends\n\nUnconstrained, `T` could be anything — which means you can't assume it has any properties at all:\n\n```ts\nfunction getLength\u003CT>(item: T): number {\n  return item.length; \u002F\u002F ❌ Property 'length' does not exist on type 'T'\n}\n```\n\nThe compiler is right to reject this: nothing says `T` has a `.length`. The fix is a **constraint** — `extends` here doesn't mean \"inherits from,\" it means \"must be assignable to\":\n\n```ts\ninterface HasLength {\n  length: number;\n}\n\nfunction getLength\u003CT extends HasLength>(item: T): number {\n  return item.length; \u002F\u002F ✅ every T that reaches this line is guaranteed to have `.length`\n}\n\ngetLength(\"hello\");        \u002F\u002F ✅ strings have .length\ngetLength([1, 2, 3]);      \u002F\u002F ✅ arrays have .length\ngetLength({ length: 10 }); \u002F\u002F ✅ any object shape with a length field\ngetLength(42);              \u002F\u002F ❌ number has no .length\n```\n\n> **Key concept:** `T extends HasLength` narrows *which types are legal*, not what `T` collapses to. `T` is still inferred per call — a `string`, an array, or a custom object — the constraint only guarantees the one property you need.\n\nA very common constraint pattern uses `keyof` to guarantee a key actually exists on an object, which is what makes a type-safe `getProperty` helper possible:\n\n```ts\nfunction getProperty\u003CT, K extends keyof T>(obj: T, key: K): T[K] {\n  return obj[key];\n}\n\nconst user = { name: \"Ada\", age: 36 };\ngetProperty(user, \"name\");   \u002F\u002F ✅ inferred as string\ngetProperty(user, \"email\");  \u002F\u002F ❌ \"email\" is not a key of { name: string; age: number }\n```\n\n`K extends keyof T` reads as \"K must be one of T's actual property names.\" Once that holds, `T[K]` — an *indexed access type* — gives you the precise type of that property, so `getProperty(user, \"age\")` returns `number`, not some generic `unknown`.\n\n## Stage 4: default type parameters\n\nA type parameter can have a default, exactly like a function parameter can have a default value — used when the caller doesn't supply one:\n\n```ts\ninterface FetchOptions\u003CTResponse = unknown> {\n  url: string;\n  parse?: (raw: string) => TResponse;\n}\n\nconst raw: FetchOptions = { url: \"\u002Fping\" };                 \u002F\u002F TResponse defaults to unknown\nconst typed: FetchOptions\u003C{ ok: boolean }> = {               \u002F\u002F explicit, overrides the default\n  url: \"\u002Fhealth\",\n  parse: (raw) => JSON.parse(raw),\n};\n```\n\nDefaults matter most on widely-used generic types where most callers don't care about every parameter — a state-management or component-library type might have four type parameters with sensible defaults so 90% of call sites only ever write the one that matters to them, and the rest fall back silently.\n\n## Stage 5: multiple type parameters and generic classes\n\nNothing restricts you to one `T`. Multiple type parameters are common wherever there's a key\u002Fvalue or input\u002Foutput relationship:\n\n```ts\nfunction zip\u003CA, B>(a: A[], b: B[]): [A, B][] {\n  return a.map((item, i) => [item, b[i]]);\n}\n\nzip([1, 2], [\"a\", \"b\"]); \u002F\u002F [number, string][] — inferred as [[1, \"a\"], [2, \"b\"]]\n```\n\nClasses take type parameters the same way functions do — on the class itself, available to every method and property inside:\n\n```ts\nclass Stack\u003CT> {\n  private items: T[] = [];\n\n  push(item: T): void {\n    this.items.push(item);\n  }\n\n  pop(): T | undefined {\n    return this.items.pop();\n  }\n\n  get size(): number {\n    return this.items.length;\n  }\n}\n\nconst numbers = new Stack\u003Cnumber>();\nnumbers.push(1);\nnumbers.push(2);\nnumbers.pop(); \u002F\u002F number | undefined\n\nconst strings = new Stack\u003Cstring>();\nstrings.push(\"a\");\n\u002F\u002F strings.push(1); \u002F\u002F ❌ number is not assignable to string\n```\n\n`T` is fixed once, at `new Stack\u003Cnumber>()`, and every method on that instance uses that same `T` for the rest of its life — `push` and `pop` stay in sync automatically, without repeating the type anywhere else in the class body.\n\n## Edge cases and gotchas\n\n- **Type erasure at runtime.** Generics are a compile-time-only construct — they're erased entirely from the emitted JavaScript. You cannot check `typeof T` or branch on `T` at runtime; if you need runtime knowledge of the type, you must pass it explicitly as a value (a constructor, a discriminant field, or a plain string tag), because by the time your code runs, `T` no longer exists.\n- **Inference can fail on object literals with contextual typing.** `identity({ name: \"Ada\" })` infers `T` as `{ name: string }`, which is often what you want, but if you need a *wider* inferred literal type (e.g. keeping a string as a specific literal rather than widening to `string`), you'll need `as const` on the argument, not a change to the generic itself.\n- **Arrow functions with generics need care in `.tsx` files.** `const identity = \u003CT>(x: T) => x;` can be parsed as a JSX opening tag inside a `.tsx` file. The common workaround is a trailing comma — `\u003CT,>(x: T) => x` — or naming a constraint explicitly, `\u003CT extends unknown>(x: T) => x`.\n- **Conditional types distribute over unions unless you stop them.** `type ToArray\u003CT> = T extends any ? T[] : never;` applied to `string | number` produces `string[] | number[]`, not `(string | number)[]`, because a naked type parameter in a conditional type distributes across each union member individually. Wrapping both sides in a tuple (`[T] extends [any] ? ...`) disables that distribution when you don't want it.\n- **A constraint is a floor, not the resolved type.** `function f\u003CT extends { id: string }>(x: T)` still infers the caller's *full* type for `T` (every extra property survives), not just `{ id: string }` — the constraint only restricts which types are legal, it doesn't narrow what gets stored in `T`.\n\n## Best practices: when (not) to reach for generics\n\n**Reach for a generic when** a function or type's inputs and outputs are *related* and that relationship needs to survive into the caller's code — a `firstElement\u003CT>` that returns the same `T` it received, a `Box\u003CT>` that wraps any payload, an `ApiResponse\u003CT>` reused across every endpoint.\n\n**Don't reach for a generic when** a union type says what you mean more simply. If a parameter only ever needs to be `\"light\" | \"dark\"`, write that union — a `\u003CT extends \"light\" | \"dark\">` generic adds ceremony without adding safety.\n\n**Don't reach for a generic to avoid writing `unknown`.** If a value's type genuinely isn't knowable until you inspect it at runtime (parsed JSON, a `catch` block's error), the honest type is `unknown` plus a runtime check — not a type parameter with no way to constrain it, and not `any`, which is how you end up back at the `firstElement` bug from the top of this article. TypeScript's own [`satisfies` operator](https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Fyour-typescript-config-objects-are-losing-type-information-satisfies-fixes-it-3hdh) solves a related but different problem — keeping a literal's narrow type while still validating it against a shape — worth knowing alongside generics, not instead of them.\n\n**Don't over-parameterize.** A function with four type parameters, three of them defaulted and never varied at any call site in your codebase, is a sign the abstraction arrived before the second real use case did. Write the concrete version first; generalize when a second caller actually needs a different type.\n\n## FAQ\n\n### What is a generic in TypeScript?\n\nA generic is a type parameter — a placeholder for a type, written in angle brackets like `\u003CT>` — that lets a function, interface, type, or class work with any type while the compiler still tracks and checks that type through every use, instead of collapsing everything to `any`.\n\n### Is `T extends SomeType` the same as class inheritance?\n\nNo. In a generic constraint, `extends` means \"must be assignable to\" or \"must have at least this shape\" — it restricts which types are legal for `T`, not a class hierarchy. `T extends { id: string }` accepts any type with an `id: string` property, not just subclasses of some `{ id: string }` class.\n\n### Can I use a default value for a generic type parameter?\n\nYes — `interface Box\u003CT = unknown>` gives `T` a default the same way a function parameter can have one. Callers that don't specify a type argument get the default; callers that do (`Box\u003Cnumber>`) override it.\n\n### Is `any` the same as a generic?\n\nNo, and this is the most common confusion. `any` disables type checking entirely for that value — TypeScript stops tracking it. A generic `T` keeps type checking fully active; it just defers *which* type until the call site, and enforces that the same `T` is used consistently everywhere it appears.\n\n### Do generic types exist at runtime?\n\nNo. TypeScript's type system, including all generics, is erased during compilation — there is no `T` in the emitted JavaScript, and you cannot inspect it with `typeof` or `instanceof` at runtime. Anything you need to know while the program is running must be represented as an actual runtime value, not just a type parameter.\n\n## Cheat sheet\n\n| Task | Syntax | Notes |\n| --- | --- | --- |\n| Generic function | `function f\u003CT>(x: T): T` | `T` inferred from the argument |\n| Generic interface\u002Ftype | `interface Box\u003CT> { value: T }` | Instantiate as `Box\u003Cnumber>` |\n| Multiple type parameters | `function zip\u003CA, B>(a: A[], b: B[])` | Each inferred independently |\n| Constrain a type parameter | `\u003CT extends HasLength>` | \"Must be assignable to,\" not inheritance |\n| Require a real key of an object | `\u003CT, K extends keyof T>` | Pairs with indexed access: `T[K]` |\n| Default type parameter | `\u003CT = unknown>` | Used when the caller omits a type argument |\n| Generic class | `class Stack\u003CT> { push(item: T): void }` | `T` fixed once, at `new Stack\u003Cnumber>()` |\n| Explicit type argument (rare) | `identity\u003Cstring>(\"hi\")` | Usually unnecessary — inference handles it |\n\n```ts\n\u002F\u002F The whole pattern, copy-paste ready\ninterface ApiResponse\u003CTData = unknown> {\n  data: TData;\n  error: string | null;\n}\n\nfunction firstMatching\u003CT, K extends keyof T>(\n  items: T[],\n  key: K,\n  value: T[K]\n): T | undefined {\n  return items.find((item) => item[key] === value);\n}\n\nclass Cache\u003CT> {\n  private store = new Map\u003Cstring, T>();\n  set(key: string, value: T): void { this.store.set(key, value); }\n  get(key: string): T | undefined { return this.store.get(key); }\n}\n\n\u002F\u002F usage — every T flows through, checked, with no `any` anywhere\nconst cache = new Cache\u003CApiResponse\u003C{ id: string }>>();\n```\n\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 7-question quiz →](https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fquiz\u002Ftake.html?post=2026-07-20-weekly-typescript-generics)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n## Key takeaways\n\n- A type parameter is a function argument for types — the compiler substitutes it at the call site and checks the relationship stays consistent, the same way it checks a value argument.\n- `extends` in a generic constraint means \"must be assignable to,\" not class inheritance — it narrows which types are legal without collapsing `T` to one fixed type.\n- Reach for a generic when an input and an output (or two inputs) are *related* and that relationship must survive into the caller's code; reach for a plain union or `unknown` when it doesn't.\n- Generics are erased at compile time — there's no `T` left at runtime, so runtime type decisions need an actual value, not a type parameter.\n- `any` and a generic solve opposite problems: `any` turns off checking, a generic keeps it on while deferring which type.\n\nYou started this article maintaining four near-identical functions, one per type, or one function typed `any` that quietly let a string crash on `.toUpperCase()`. Now you have the one mechanism — a type parameter, substituted and checked at every call site — that replaces both: a single `firstElement\u003CT>`, a single `Cache\u003CT>`, a single `ApiResponse\u003CT>`, each one exactly as safe as if you'd hand-written it for every type you'll ever call it with. Where's the first place in your own codebase you'll delete a copy-pasted function and replace it with one generic? Tell me in the comments.\n\n---\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)\n- 💼 **LinkedIn** — [linkedin.com\u002Fin\u002Fparsa-jiravand](https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F)\n- ✉️ **Email** (work & contract inquiries): [bestpractice2026@gmail.com](mailto:bestpractice2026@gmail.com)",{"title":35,"canonical":437,"description":37},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fweekly-typescript-generics","019fe660-57eb-7258-b452-d93e631b12b5",{"id":440,"locked":18},"019fe660-5ece-75f7-97dc-27d2d22663f3",[442],{"id":443,"slug":34,"title":444,"_count":445},"019fe776-6408-72ff-8ac3-baa2e75b42c6","TypeScript Generics",{"questions":446},7,[448],{"locale":13,"slug":34},{"id":443,"slug":34,"title":444,"_count":450,"questionCount":446},{"questions":446},"\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">firstElement\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>arr\u003Cspan class=\"shj-type\">: any\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-type\">: any\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> arr\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> n \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">firstElement\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">2\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">3\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;      \u003Cspan class=\"shj-cmnt\">\u002F\u002F typed as `any` — no autocomplete, no error checking\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> s \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">firstElement\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"a\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"b\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;     \u003Cspan class=\"shj-cmnt\">\u002F\u002F also `any` — TypeScript has no idea it's a string\n\u003C\u002Fspan>n\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">toUpperCase\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;                        \u003Cspan class=\"shj-cmnt\">\u002F\u002F compiles fine, crashes at runtime: not a string\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">firstNumber\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>arr\u003Cspan class=\"shj-type\">: number\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-type\">: number\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> arr\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>; \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">firstString\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>arr\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> arr\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>; \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F ...one more function for every type you ever call this with\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F a regular function: `x` is a placeholder for a VALUE, filled in at the call site\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">double\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>x\u003Cspan class=\"shj-type\">: number\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-type\">: number\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> x \u003Cspan class=\"shj-oper\">*\u003C\u002Fspan> \u003Cspan class=\"shj-num\">2\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-func\">double\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">5\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F x = 5\n\u003C\u002Fspan>\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F a generic function: `T` is a placeholder for a TYPE, filled in at the call site\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> identity\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>x\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> x;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-func\">identity\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">5\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;      \u003Cspan class=\"shj-cmnt\">\u002F\u002F T = number\n\u003C\u002Fspan>\u003Cspan class=\"shj-func\">identity\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"hi\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;   \u003Cspan class=\"shj-cmnt\">\u002F\u002F T = string\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> firstElement\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>arr\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">|\u003C\u002Fspan> \u003Cspan class=\"shj-num\">undefined\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> arr\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> n \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">firstElement\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">2\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">3\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;    \u003Cspan class=\"shj-cmnt\">\u002F\u002F n: number | undefined\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> s \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">firstElement\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"a\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"b\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;   \u003Cspan class=\"shj-cmnt\">\u002F\u002F s: string | undefined\n\u003C\u002Fspan>n\u003Cspan class=\"shj-oper\">?.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">toFixed\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">2\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;                        \u003Cspan class=\"shj-cmnt\">\u002F\u002F ✅ compiler knows n might be a number\n\u003C\u002Fspan>s\u003Cspan class=\"shj-oper\">?.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">toUpperCase\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;                     \u003Cspan class=\"shj-cmnt\">\u002F\u002F ✅ compiler knows s might be a string\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> mapArray\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">U\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>arr\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> fn\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>item\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-num\">U\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">U\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> arr\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">map\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>fn\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> lengths \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">mapArray\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"a\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"bb\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"ccc\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>s\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> s\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F lengths: number[]\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">interface\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Box\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  value\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> numberBox\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Box\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>number\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> value\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">42\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> stringBox\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Box\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>string\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> value\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"hello\"\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F const bad: Box&lt;number&gt; = { value: \"hello\" }; \u002F\u002F ❌ string is not assignable to number\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">type\u003C\u002Fspan> \u003Cspan class=\"shj-class\">ApiResponse\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  data\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>;\n  error\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">|\u003C\u002Fspan> \u003Cspan class=\"shj-num\">null\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">async\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">getUser\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>id\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Promise\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-class\">ApiResponse\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-class\">User\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-cmnt\">\u002F\u002F ...\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> getLength\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>item\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-type\">: number\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> item\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length; \u003Cspan class=\"shj-cmnt\">\u002F\u002F ❌ Property 'length' does not exist on type 'T'\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">interface\u003C\u002Fspan> \u003Cspan class=\"shj-class\">HasLength\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  length\u003Cspan class=\"shj-type\">: number\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> getLength\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-num\">T\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">extends\u003C\u002Fspan> \u003Cspan class=\"shj-class\">HasLength\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>item\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-type\">: number\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> item\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length; \u003Cspan class=\"shj-cmnt\">\u002F\u002F ✅ every T that reaches this line is guaranteed to have `.length`\n\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-func\">getLength\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"hello\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;        \u003Cspan class=\"shj-cmnt\">\u002F\u002F ✅ strings have .length\n\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getLength\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">2\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">3\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;      \u003Cspan class=\"shj-cmnt\">\u002F\u002F ✅ arrays have .length\n\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getLength\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> length\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">10\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F ✅ any object shape with a length field\n\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getLength\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">42\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;              \u003Cspan class=\"shj-cmnt\">\u002F\u002F ❌ number has no .length\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> getProperty\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">K\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">extends\u003C\u002Fspan> keyof \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>obj\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> key\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">K\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-num\">K\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> obj\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>key\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> user \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> name\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"Ada\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> age\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">36\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\n\u003Cspan class=\"shj-func\">getProperty\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>user\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"name\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;   \u003Cspan class=\"shj-cmnt\">\u002F\u002F ✅ inferred as string\n\u003C\u002Fspan>\u003Cspan class=\"shj-func\">getProperty\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>user\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"email\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;  \u003Cspan class=\"shj-cmnt\">\u002F\u002F ❌ \"email\" is not a key of { name: string; age: number }\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">interface\u003C\u002Fspan> \u003Cspan class=\"shj-class\">FetchOptions\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-class\">TResponse\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> unknown\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  url\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan>;\n  parse\u003Cspan class=\"shj-oper\">?:\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>raw\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-class\">TResponse\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> raw\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-class\">FetchOptions\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> url\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"\u002Fping\"\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;                 \u003Cspan class=\"shj-cmnt\">\u002F\u002F TResponse defaults to unknown\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> typed\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-class\">FetchOptions\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> ok\u003Cspan class=\"shj-type\">: boolean\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>               \u003Cspan class=\"shj-cmnt\">\u002F\u002F explicit, overrides the default\n\u003C\u002Fspan>  url\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"\u002Fhealth\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  parse\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>raw\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-num\">JSON\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">parse\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>raw\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> zip\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-num\">A\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">B\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>a\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">A\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> b\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">B\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-num\">A\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">B\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> a\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">map\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>item\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> i\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>item\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> b\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>i\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-func\">zip\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">2\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"a\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"b\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F [number, string][] — inferred as [[1, \"a\"], [2, \"b\"]]\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003Cdiv>19\u003C\u002Fdiv>\u003Cdiv>20\u003C\u002Fdiv>\u003Cdiv>21\u003C\u002Fdiv>\u003Cdiv>22\u003C\u002Fdiv>\u003Cdiv>23\u003C\u002Fdiv>\u003Cdiv>24\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">class\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Stack\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">private\u003C\u002Fspan> items\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>;\n\n  \u003Cspan class=\"shj-func\">push\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>item\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-type\">: void\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">this\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>items\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">push\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>item\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n  \u003Cspan class=\"shj-func\">pop\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">|\u003C\u002Fspan> \u003Cspan class=\"shj-num\">undefined\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">this\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>items\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">pop\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n  \u003Cspan class=\"shj-kwd\">get\u003C\u002Fspan> \u003Cspan class=\"shj-func\">size\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-type\">: number\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">this\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>items\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> numbers \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Stack\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>number\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\nnumbers\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">push\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\nnumbers\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">push\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">2\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\nnumbers\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">pop\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F number | undefined\n\u003C\u002Fspan>\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> strings \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Stack\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>string\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\nstrings\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">push\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"a\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F strings.push(1); \u002F\u002F ❌ number is not assignable to string\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-ts shj-multiline\" data-lang=\"ts\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003Cdiv>19\u003C\u002Fdiv>\u003Cdiv>20\u003C\u002Fdiv>\u003Cdiv>21\u003C\u002Fdiv>\u003Cdiv>22\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F The whole pattern, copy-paste ready\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">interface\u003C\u002Fspan> \u003Cspan class=\"shj-class\">ApiResponse\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-class\">TData\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> unknown\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  data\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-class\">TData\u003C\u002Fspan>;\n  error\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">|\u003C\u002Fspan> \u003Cspan class=\"shj-num\">null\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> firstMatching\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">K\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">extends\u003C\u002Fspan> keyof \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n  items\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  key\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">K\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  value\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-num\">K\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">|\u003C\u002Fspan> \u003Cspan class=\"shj-num\">undefined\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> items\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">find\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>item\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> item\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>key\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> value\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-kwd\">class\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Cache\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">private\u003C\u002Fspan> store \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Map\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>string\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">set\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>key\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> value\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-type\">: void\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">this\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>store\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">set\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>key\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> value\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">get\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>key\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">T\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">|\u003C\u002Fspan> \u003Cspan class=\"shj-num\">undefined\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">this\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>store\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">get\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>key\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F usage — every T flows through, checked, with no `any` anywhere\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> cache \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Cache\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-class\">ApiResponse\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> id\u003Cspan class=\"shj-type\">: string\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>"]