[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"header-categories":3,"post-typescript-generics-demystified":73,"parsed-post-f3db841d-5307-41c5-97b1-f757bd4bf72a":142},{"success":4,"data":5},true,{"items":6,"pagination":70},[7,16,24,32,40,48,56,63],{"id":8,"name":9,"description":10,"parentId":11,"createdAt":12,"updatedAt":12,"parent":11,"children":13,"_count":14},"ai-future","AI & The Future","Artificial intelligence and emerging technology",null,"2026-07-24T06:02:45.990Z",[],{"posts":15},1,{"id":17,"name":18,"description":19,"parentId":11,"createdAt":20,"updatedAt":20,"parent":11,"children":21,"_count":22},"tools-productivity","Tools & Productivity","Developer tools and workflow optimization","2026-07-24T06:02:45.712Z",[],{"posts":23},7,{"id":25,"name":26,"description":27,"parentId":11,"createdAt":28,"updatedAt":28,"parent":11,"children":29,"_count":30},"career","Career & Growth","Professional development for developers","2026-07-24T06:02:45.486Z",[],{"posts":31},3,{"id":33,"name":34,"description":35,"parentId":11,"createdAt":36,"updatedAt":36,"parent":11,"children":37,"_count":38},"opinion","Opinion & Analysis","Industry analysis and technical opinions","2026-07-24T06:02:45.272Z",[],{"posts":39},6,{"id":41,"name":42,"description":43,"parentId":11,"createdAt":44,"updatedAt":44,"parent":11,"children":45,"_count":46},"tutorials","Tutorials","Step-by-step learning guides","2026-07-24T06:02:44.965Z",[],{"posts":47},21,{"id":49,"name":50,"description":51,"parentId":11,"createdAt":52,"updatedAt":52,"parent":11,"children":53,"_count":54},"software-engineering","Software Engineering","Best practices, patterns, and principles","2026-07-24T06:02:44.689Z",[],{"posts":55},24,{"id":57,"name":58,"description":59,"parentId":11,"createdAt":60,"updatedAt":60,"parent":11,"children":61,"_count":62},"devops-cloud","DevOps & Cloud","Infrastructure, deployment, and cloud computing","2026-07-24T06:02:44.453Z",[],{"posts":39},{"id":64,"name":65,"description":66,"parentId":11,"createdAt":67,"updatedAt":67,"parent":11,"children":68,"_count":69},"web-development","Web Development","Frontend and backend web development tutorials and guides","2026-07-24T06:02:44.169Z",[],{"posts":55},{"page":15,"limit":71,"total":71,"totalPages":15,"hasNext":72,"hasPrev":72},8,false,{"success":4,"data":74},{"id":75,"slug":76,"title":77,"excerpt":78,"content":79,"status":80,"visibility":81,"featuredImage":82,"canonicalUrl":11,"readingTime":83,"viewCount":84,"commentEnabled":4,"publishedAt":85,"scheduledAt":11,"createdAt":86,"updatedAt":87,"seoTitle":88,"seoDescription":78,"seoKeywords":11,"authorId":89,"author":90,"coAuthors":95,"tags":96,"categories":107,"comments":110,"_count":111,"relatedPosts":113},"f3db841d-5307-41c5-97b1-f757bd4bf72a","typescript-generics-demystified","TypeScript Generics Demystified: From Basics to Advanced Patterns","Finally understand TypeScript generics. From simple type parameters to conditional types and mapped types with real-world examples.","## Why Generics?\n\nGenerics let you write code that works with any type while still maintaining type safety. Without them, you'd either lose type information or duplicate code for every type.\n\n## The Basics\n\n```typescript\n\u002F\u002F Without generics — loses type info\nfunction first(arr: any[]): any {\n  return arr[0]\n}\n\n\u002F\u002F With generics — preserves type\nfunction first\u003CT>(arr: T[]): T | undefined {\n  return arr[0]\n}\n\nconst num = first([1, 2, 3])      \u002F\u002F type: number\nconst str = first(['a', 'b'])      \u002F\u002F type: string\n```\n\n## Constraints\n\nLimit what types are accepted:\n\n```typescript\ninterface HasId {\n  id: string\n}\n\nfunction findById\u003CT extends HasId>(items: T[], id: string): T | undefined {\n  return items.find(item => item.id === id)\n}\n\n\u002F\u002F Works with any object that has an id\nfindById(users, '123')  \u002F\u002F returns User | undefined\nfindById(posts, '456')  \u002F\u002F returns Post | undefined\n```\n\n## Multiple Type Parameters\n\n```typescript\nfunction merge\u003CA, B>(objA: A, objB: B): A & B {\n  return { ...objA, ...objB }\n}\n\nconst result = merge({ name: 'Mbeah' }, { role: 'developer' })\n\u002F\u002F type: { name: string } & { role: string }\n```\n\n## Real-World: Type-Safe Event Emitter\n\n```typescript\ntype EventMap = {\n  'user:login': { userId: string; timestamp: number }\n  'user:logout': { userId: string }\n  'post:created': { postId: string; title: string }\n}\n\nclass TypedEmitter\u003CEvents extends Record\u003Cstring, any>> {\n  private handlers = new Map\u003Cstring, Function[]>()\n\n  on\u003CK extends keyof Events>(event: K, handler: (payload: Events[K]) => void) {\n    const list = this.handlers.get(event as string) || []\n    list.push(handler)\n    this.handlers.set(event as string, list)\n  }\n\n  emit\u003CK extends keyof Events>(event: K, payload: Events[K]) {\n    const list = this.handlers.get(event as string) || []\n    list.forEach(fn => fn(payload))\n  }\n}\n\nconst emitter = new TypedEmitter\u003CEventMap>()\n\nemitter.on('user:login', (payload) => {\n  \u002F\u002F payload is typed as { userId: string; timestamp: number }\n  console.log(payload.userId)\n})\n\n\u002F\u002F emitter.emit('user:login', { userId: '123' })\n\u002F\u002F ❌ Error: missing 'timestamp'\n```\n\n::callout{icon=\"i-lucide-lightbulb\" color=\"success\"}\nGeneric constraints with `keyof` ensure that event names and their payloads always match. Impossible to emit the wrong data shape.\n::\n\n## Conditional Types\n\n```typescript\ntype IsArray\u003CT> = T extends any[] ? true : false\n\ntype A = IsArray\u003Cstring[]>  \u002F\u002F true\ntype B = IsArray\u003Cnumber>    \u002F\u002F false\n\n\u002F\u002F Practical: extract promise value\ntype Unwrap\u003CT> = T extends Promise\u003Cinfer U> ? U : T\n\ntype X = Unwrap\u003CPromise\u003Cstring>>  \u002F\u002F string\ntype Y = Unwrap\u003Cnumber>           \u002F\u002F number\n```\n\n## Mapped Types\n\n```typescript\n\u002F\u002F Make all properties optional\ntype Partial\u003CT> = {\n  [K in keyof T]?: T[K]\n}\n\n\u002F\u002F Make all properties readonly\ntype Readonly\u003CT> = {\n  readonly [K in keyof T]: T[K]\n}\n\n\u002F\u002F Pick specific properties\ntype ApiResponse\u003CT> = {\n  [K in keyof T as `get${Capitalize\u003CK & string>}`]: () => T[K]\n}\n\ntype UserApi = ApiResponse\u003C{ name: string; age: number }>\n\u002F\u002F { getName: () => string; getAge: () => number }\n```\n\n## Key Insight\n\nThink of generics as **functions for types**. Just as regular functions take values and return values, generic types take types and return types.\n\nStart simple, add constraints when needed, and let TypeScript's inference do the heavy lifting.\n","published","public","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1550439062-609e1531270e?w=1200&q=80",12,913,"2026-06-05T06:03:44.378Z","2026-07-24T06:03:44.381Z","2026-07-28T17:11:32.182Z","TypeScript Generics Demystified: From Basics to Advanced Patterns | BitBlog","fddb5d93-7a2c-4d86-a06a-fa32e73a01c6",{"email":91,"bio":92,"id":89,"name":93,"avatarUrl":94},"mbeahessilfieprince@gmail.com","Fullstack Software Developer ","Mbeah Essilfie","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F93322394?v=4",[],[97,102],{"id":98,"name":99,"color":100,"description":101},"javascript","JavaScript","#f7df1e","The language of the web",{"id":103,"name":104,"color":105,"description":106},"typescript","TypeScript","#3178c6","Typed superset of JavaScript",[108,109],{"id":64,"name":65,"description":66},{"id":41,"name":42,"description":43},[],{"comments":112},0,[114,123,133],{"id":115,"slug":116,"title":117,"excerpt":118,"featuredImage":119,"viewCount":120,"readingTime":71,"publishedAt":121,"author":122},"2997028f-4d22-4eb7-9d86-894a54cb559a","building-type-safe-apis-nuxt3-prisma","Building Type-Safe APIs with Nuxt 3 and Prisma","Learn how to build end-to-end type-safe APIs using Nuxt 3 server routes and Prisma ORM for a seamless developer experience.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1498050108023-c5249f4df085?w=1200&q=80",1922,"2026-07-23T06:02:46.910Z",{"id":89,"name":93,"avatarUrl":94},{"id":124,"slug":125,"title":126,"excerpt":127,"featuredImage":128,"viewCount":129,"readingTime":130,"publishedAt":131,"author":132},"da9e4553-8b0d-411e-b4ec-f9684017e163","mastering-tailwind-css-design-systems","Mastering Tailwind CSS: From Utility Classes to Design Systems","Go beyond basic utility classes and learn how to build cohesive, maintainable design systems with Tailwind CSS.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1555066931-4365d14bab8c?w=1200&q=80",257,10,"2026-07-21T06:02:49.268Z",{"id":89,"name":93,"avatarUrl":94},{"id":134,"slug":135,"title":136,"excerpt":137,"featuredImage":138,"viewCount":139,"readingTime":83,"publishedAt":140,"author":141},"50add5fc-4026-4267-b917-7f71ea9e35b0","complete-guide-vue3-composables","The Complete Guide to Vue 3 Composables","Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1517694712202-14dd9538aa97?w=1200&q=80",990,"2026-07-19T06:02:51.753Z",{"id":89,"name":93,"avatarUrl":94},{"data":143,"body":145,"toc":304},{"title":144,"description":144},"",{"type":146,"children":147},"root",[148,157,163,169,181,187,192,200,206,214,220,228,247,253,261,267,275,281,294,299],{"type":149,"tag":150,"props":151,"children":153},"element","h2",{"id":152},"why-generics",[154],{"type":155,"value":156},"text","Why Generics?",{"type":149,"tag":158,"props":159,"children":160},"p",{},[161],{"type":155,"value":162},"Generics let you write code that works with any type while still maintaining type safety. Without them, you'd either lose type information or duplicate code for every type.",{"type":149,"tag":150,"props":164,"children":166},{"id":165},"the-basics",[167],{"type":155,"value":168},"The Basics",{"type":149,"tag":170,"props":171,"children":175},"pre",{"className":172,"code":173,"language":103,"meta":144,"style":174},"language-typescript","\u002F\u002F Without generics — loses type info\nfunction first(arr: any[]): any {\n  return arr[0]\n}\n\n\u002F\u002F With generics — preserves type\nfunction first\u003CT>(arr: T[]): T | undefined {\n  return arr[0]\n}\n\nconst num = first([1, 2, 3])      \u002F\u002F type: number\nconst str = first(['a', 'b'])      \u002F\u002F type: string\n","undefined",[176],{"type":149,"tag":177,"props":178,"children":179},"code",{"__ignoreMap":144},[180],{"type":155,"value":173},{"type":149,"tag":150,"props":182,"children":184},{"id":183},"constraints",[185],{"type":155,"value":186},"Constraints",{"type":149,"tag":158,"props":188,"children":189},{},[190],{"type":155,"value":191},"Limit what types are accepted:",{"type":149,"tag":170,"props":193,"children":195},{"className":172,"code":194,"language":103,"meta":144,"style":174},"interface HasId {\n  id: string\n}\n\nfunction findById\u003CT extends HasId>(items: T[], id: string): T | undefined {\n  return items.find(item => item.id === id)\n}\n\n\u002F\u002F Works with any object that has an id\nfindById(users, '123')  \u002F\u002F returns User | undefined\nfindById(posts, '456')  \u002F\u002F returns Post | undefined\n",[196],{"type":149,"tag":177,"props":197,"children":198},{"__ignoreMap":144},[199],{"type":155,"value":194},{"type":149,"tag":150,"props":201,"children":203},{"id":202},"multiple-type-parameters",[204],{"type":155,"value":205},"Multiple Type Parameters",{"type":149,"tag":170,"props":207,"children":209},{"className":172,"code":208,"language":103,"meta":144,"style":174},"function merge\u003CA, B>(objA: A, objB: B): A & B {\n  return { ...objA, ...objB }\n}\n\nconst result = merge({ name: 'Mbeah' }, { role: 'developer' })\n\u002F\u002F type: { name: string } & { role: string }\n",[210],{"type":149,"tag":177,"props":211,"children":212},{"__ignoreMap":144},[213],{"type":155,"value":208},{"type":149,"tag":150,"props":215,"children":217},{"id":216},"real-world-type-safe-event-emitter",[218],{"type":155,"value":219},"Real-World: Type-Safe Event Emitter",{"type":149,"tag":170,"props":221,"children":223},{"className":172,"code":222,"language":103,"meta":144,"style":174},"type EventMap = {\n  'user:login': { userId: string; timestamp: number }\n  'user:logout': { userId: string }\n  'post:created': { postId: string; title: string }\n}\n\nclass TypedEmitter\u003CEvents extends Record\u003Cstring, any>> {\n  private handlers = new Map\u003Cstring, Function[]>()\n\n  on\u003CK extends keyof Events>(event: K, handler: (payload: Events[K]) => void) {\n    const list = this.handlers.get(event as string) || []\n    list.push(handler)\n    this.handlers.set(event as string, list)\n  }\n\n  emit\u003CK extends keyof Events>(event: K, payload: Events[K]) {\n    const list = this.handlers.get(event as string) || []\n    list.forEach(fn => fn(payload))\n  }\n}\n\nconst emitter = new TypedEmitter\u003CEventMap>()\n\nemitter.on('user:login', (payload) => {\n  \u002F\u002F payload is typed as { userId: string; timestamp: number }\n  console.log(payload.userId)\n})\n\n\u002F\u002F emitter.emit('user:login', { userId: '123' })\n\u002F\u002F ❌ Error: missing 'timestamp'\n",[224],{"type":149,"tag":177,"props":225,"children":226},{"__ignoreMap":144},[227],{"type":155,"value":222},{"type":149,"tag":229,"props":230,"children":233},"callout",{"color":231,"icon":232},"success","i-lucide-lightbulb",[234],{"type":149,"tag":158,"props":235,"children":236},{},[237,239,245],{"type":155,"value":238},"Generic constraints with ",{"type":149,"tag":177,"props":240,"children":242},{"className":241},[],[243],{"type":155,"value":244},"keyof",{"type":155,"value":246}," ensure that event names and their payloads always match. Impossible to emit the wrong data shape.",{"type":149,"tag":150,"props":248,"children":250},{"id":249},"conditional-types",[251],{"type":155,"value":252},"Conditional Types",{"type":149,"tag":170,"props":254,"children":256},{"className":172,"code":255,"language":103,"meta":144,"style":174},"type IsArray\u003CT> = T extends any[] ? true : false\n\ntype A = IsArray\u003Cstring[]>  \u002F\u002F true\ntype B = IsArray\u003Cnumber>    \u002F\u002F false\n\n\u002F\u002F Practical: extract promise value\ntype Unwrap\u003CT> = T extends Promise\u003Cinfer U> ? U : T\n\ntype X = Unwrap\u003CPromise\u003Cstring>>  \u002F\u002F string\ntype Y = Unwrap\u003Cnumber>           \u002F\u002F number\n",[257],{"type":149,"tag":177,"props":258,"children":259},{"__ignoreMap":144},[260],{"type":155,"value":255},{"type":149,"tag":150,"props":262,"children":264},{"id":263},"mapped-types",[265],{"type":155,"value":266},"Mapped Types",{"type":149,"tag":170,"props":268,"children":270},{"className":172,"code":269,"language":103,"meta":144,"style":174},"\u002F\u002F Make all properties optional\ntype Partial\u003CT> = {\n  [K in keyof T]?: T[K]\n}\n\n\u002F\u002F Make all properties readonly\ntype Readonly\u003CT> = {\n  readonly [K in keyof T]: T[K]\n}\n\n\u002F\u002F Pick specific properties\ntype ApiResponse\u003CT> = {\n  [K in keyof T as `get${Capitalize\u003CK & string>}`]: () => T[K]\n}\n\ntype UserApi = ApiResponse\u003C{ name: string; age: number }>\n\u002F\u002F { getName: () => string; getAge: () => number }\n",[271],{"type":149,"tag":177,"props":272,"children":273},{"__ignoreMap":144},[274],{"type":155,"value":269},{"type":149,"tag":150,"props":276,"children":278},{"id":277},"key-insight",[279],{"type":155,"value":280},"Key Insight",{"type":149,"tag":158,"props":282,"children":283},{},[284,286,292],{"type":155,"value":285},"Think of generics as ",{"type":149,"tag":287,"props":288,"children":289},"strong",{},[290],{"type":155,"value":291},"functions for types",{"type":155,"value":293},". Just as regular functions take values and return values, generic types take types and return types.",{"type":149,"tag":158,"props":295,"children":296},{},[297],{"type":155,"value":298},"Start simple, add constraints when needed, and let TypeScript's inference do the heavy lifting.",{"type":149,"tag":300,"props":301,"children":302},"style",{},[303],{"type":155,"value":144},{"title":144,"searchDepth":305,"depth":305,"links":306},2,[307,308,309,310,311,312,313,314],{"id":152,"depth":305,"text":156},{"id":165,"depth":305,"text":168},{"id":183,"depth":305,"text":186},{"id":202,"depth":305,"text":205},{"id":216,"depth":305,"text":219},{"id":249,"depth":305,"text":252},{"id":263,"depth":305,"text":266},{"id":277,"depth":305,"text":280}]