Zod is the best thing to happen to TypeScript APIs since TypeScript itself. I spent 3 years writing manual validation logic in Node.js APIs. Checking if req.body.email is a string. Checking if it's actually an email. Checking if req.body.age is a number and not negative. Writing the error message manually. Remembering to do this on every route. Then I found Zod. I genuinely don't know how I shipped APIs without it. WHAT ZOD DOES Zod lets you define a schema once. That schema does three things: 1. Validates the data at runtime 2. Infers the TypeScript type automatically 3. Produces clean, structured error messages // One schema. Three things at once. import { z } from 'zod' const CreateOrderSchema = z.object({ userId: z.string().uuid(), items: z.array(z.object({ productId: z.string().uuid(), quantity: z.number().int().min(1).max(100) })).min(1, 'Order must have at least one item'), deliveryDate: z.string().datetime().optional(), promoCode: z.string().toUpperCase().optional() }) // TypeScript type — inferred automatically, no duplication type CreateOrder = z.infer USING IT IN AN EXPRESS / NESTJS API const result = CreateOrderSchema.safeParse(req.body) if (!result.success) { return res.status(422).json({ errors: result.error.flatten().fieldErrors }) // Returns exactly which field failed and why // { items: ['Order must have at least one item'] } } // result.data is now fully typed — no casting, no assertions const order = await orderService.create(result.data) 3 ZOD PATTERNS I USE ON EVERY PROJECT 1. .transform() — sanitise on parse, not separately z.string().trim().toLowerCase().email() 2. .refine() — custom logic type-safety can't express z.string().refine(s => isValidIBAN(s), 'Invalid IBAN') 3. Shared schemas between frontend and backend One package, one source of truth, zero API contract drift Zod replaced about 400 lines of manual validation in the last codebase I cleaned up. 400 lines that were inconsistent, untested, and spread across 30 files. One Zod schema file. Consistent everywhere. #TypeScript #NodeJS #WebDevelopment #BackendDevelopment #SoftwareEngineering
Zod Simplifies TypeScript API Validation
More Relevant Posts
-
𝗧𝘆𝗽𝗲𝘀𝗰𝗿𝗶𝗽𝘁 𝗧𝘆𝗽𝗲𝘀: 𝗕𝗲𝘆𝗼𝗻𝗱 𝗕𝗮𝘀𝗶𝗰𝘀 You write TypeScript. You avoid any types. You see green squiggles. But errors happen at runtime. TypeScript missed them. Many developers face this. Basic types seem enough. But TypeScript can catch more. It makes code stronger. Use these patterns. - Branded types: Give strings unique names. UserId and Email are both strings but different types. This stops mix-ups. type UserId = string & { brand: unique symbol }; function createUserId(id: string): UserId { return id as UserId; } function sendEmail(userId: UserId, email: Email) {} sendEmail(createUserId('123'), 'test@example.com'); // Error - Template literals and infer: Parse string patterns. Extract parts from paths. type Route = `/users/${string}/posts/${string}`; type Params<T> = T extends `/users/${infer U}/posts/${infer P}` ? {userId: U; postId: P} : never; Params<'/users/alice/posts/99'> is {userId: 'alice', postId: '99'} - as const: Make objects deeply immutable. Get exact types. const config = { env: 'production', retries: 3 } as const; // config.env is 'production' not string - keyof and mapped types: Generate types from keys. Create Partial or Readonly. type PartialUser = { [K in keyof User]?: User[K]; }; type UserUpdate = { [K in keyof User as K extends 'id' ? never : K]?: User[K]; }; - Conditional types: Add logic to types. type Awaited<T> = T extends Promise<infer U> ? U : T; type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T; Build a type-safe API client. const API_ROUTES = { getUser: '/users/:id' } as const; type ParamsFor<K extends keyof typeof API_ROUTES> = Record<ExtractParamNames<typeof API_ROUTES[K]>, string>; async function fetchFromApi<K extends keyof typeof API_ROUTES>(key: K, params: ParamsFor<K>) { // Fetch with correct params } This finds param errors early. Advanced TypeScript shifts your mindset. Design types before code. Start with one pattern. Apply it today. Catch bugs early. Refactor with confidence. What patterns do you use? Share your stories below. Source: https://lnkd.in/g__X8SbJ
To view or add a comment, sign in
-
𝗧𝘆𝗽𝗲𝗦𝗰𝗿𝗶𝗽𝘁 𝟲.𝟬 𝗞𝗲𝘆 𝗙𝗲𝗮𝘁𝘂𝗿𝗲𝘀 𝗳𝗼𝗿 𝟮𝟬𝟮𝟲 TypeScript 6.0 is here. It sets up major changes for TypeScript 7.0. Here is what matters for your code. **Small fix, big help** TypeScript 6.0 now understands method syntax better. Functions that do not use `this` get better type guesses. Your code works consistently now. **Cleaner imports** You can now use `#/` as an import prefix. This matches bundler tools. Write `import utils from "#/utils.js"` instead of `../../../utils.js`. **Predictable output** A new flag `--stableTypeOrdering` makes union types appear in a fixed order. Your build files will not change randomly. Note: this can slow compilation. **Modern dates** The Temporal API is now built-in. It fixes many problems with JavaScript's Date object. Use it for time zones and durations. **Easier Map updates** Maps have two new methods: - `getOrInsert(key, default)` sets a value if missing. - `getOrInsertComputed(key, function)` runs a function only if the key is missing. **Safer regex** Use `RegExp.escape(userInput)` to safely turn user text into a pattern. You avoid injection bugs. **Breaking changes** New defaults in `tsconfig.json`: - `strict: true` - `module: esnext` - `target: es2025` (floating) - `types: []` (empty, add needed types yourself) You must now list required types explicitly. Options like `baseUrl` are deprecated. Use full paths in `paths` instead. **Your action plan** Immediate: - Update your `tsconfig.json` - Add needed types to the `types` array - Set `rootDir` if your source folder is not the project root Medium term: - Move off `target: es5` - Change module resolution to `nodenext` or `bundler` - Stop using AMD or UMD modules Long term: - Test the `--stableTypeOrdering` flag - Plan for parallel builds in CI when TypeScript 7.0 arrives These changes prepare your project for faster builds. The default settings cut startup time. Source: https://lnkd.in/gu9jP4tb Which feature will you try first? Share your migration steps below.
To view or add a comment, sign in
-
I've reviewed hundreds of JS/TS codebases over 9 years. The same 20 mistakes show up everywhere, from juniors to seniors. Here they are 👇 1. Not enabling TypeScript strict mode. ↳ You're writing JavaScript with extra syntax. Turn it on. Real bugs hide in loose types. 2. Using `any` as an escape hatch. ↳ Every `any` is a hole in your type safety. Use `unknown`. For external payloads, use Zod. 3. Not using discriminated unions. ↳ Optional fields aren't a type system. Discriminated unions narrow types perfectly. 4. Ignoring return types on exported functions. ↳ Let internals infer. Annotate exports, that's your API contract. 5. Catching errors and swallowing them. ↳ `catch (e) { console.log(e) }` isn't handling. Rethrow, recover, or transform. 6. Not handling Promise rejections. ↳ Node.js terminates on unhandled rejections. Handle or propagate, always. 7. Hardcoding dependencies instead of injecting them. ↳ Direct imports make unit tests painful. Pass deps in. Hours of mocking pain saved. 8. Over-engineering with microservices too early. ↳ 3 devs, 500 users, 7 services? Modular monolith first. Split when you have a reason. 9. Writing 100+ line functions. ↳ Too long means too many things. Extract and name the pieces. 10. Putting business logic in controllers. ↳ Handlers parse, delegate, respond. Business rules live in services. 11. Circular dependencies between modules. ↳ Cyclic imports hand the second module `undefined` bindings. Top-level use (`extends`, `new`) crashes; inside methods, it works until someone adds a top-level reference. 12. Importing everything from barrel files. ↳ `index.ts` re-exports break tree-shaking. Import from source files directly. 13. Mutating function parameters. ↳ Mutations cause action-at-a-distance bugs. Return new objects. 14. Not using `readonly`. ↳ Lock what shouldn't change. `Readonly<T>` is shallow. Arrays need `readonly string[]`. 15. Leaking memory with uncleared listeners and timers. ↳ Every `addEventListener` needs a `removeEventListener`. Every `setInterval`, a `clearInterval`. A `connect()`, a `disconnect()`. 16. Never canceling async operations. ↳ Without AbortController: wasted requests, stale data. In React, the #1 cause of old-data bugs. 17. Running independent async ops sequentially. ↳ Use `Promise.all` when they don't depend on each other. Don't turn 3×1s into 3s. 18. Using `Date` for everything. ↳ Same string parses differently on server vs. browser. Use `date-fns` or `Temporal`. 19. Testing for coverage, not for value. ↳ 100% coverage is vanity. Test behavior and edge cases. 20. Not validating input at the boundary. ↳ Validate `req.body` with Zod. Strips unknown fields. Trust types after. How many are in your codebase right now? —— 💾 Save this for later. ♻ Repost to help others find it. ➕ Follow Petar Ivanov + turn on notifications. #javascript
To view or add a comment, sign in
-
-
Do you ever know exactly what type a dynamic object is, but TypeScript won’t trust you? 🤔Here is #Day 21of #TypescriptBeforeAngular with SANDEEP KUMAR(#IAM5K): Type Assertions (as) (The "Manual Override") Like when you are accessing a standard DOM element (ViewChild) in Angular, and TypeScript only sees it as a generic Element, so it won’t let you use the .value or .focus() properties. If you are just using any to "silence" these errors, you are disabling your safety net and creating a massive bug trap. When you are certain about a type that the compiler cannot know yet, you need the authorized manual override: Type Assertions (as). 1️⃣ What is a Type Assertion? Sometimes, based on the context, you know more about the shape of a value than #TypeScript can ever determine. Think of a Type Assertion as a "Trust Badge" or a "Manual Override Key" that you present to the compiler. You use the as keyword to tell TypeScript: "I promise you, I know exactly what I'm doing. From this line onward, please treat this generic bucket as this specific, detailed type." // --- ☠️ The DANGEROUS way (any) --- const myElementAny: any = document.getElementById('user-email'); // No help here. If 'myElementAny' is actually null, this crashes later: console.log(myElementAny.value); // --- 🟢 The SAFE way (assertion) --- const myInput = document.getElementById('user-email') as HTMLInputElement; // Now TypeScript knows this bucket has specific input powers: myInput.value = 'hello@example.com'; // ✅ Perfect autocomplete! 2️⃣ Significance in Angular: In modern Angular (especially for state management and DOM interaction), Type Assertions are essential when you are dealing with dynamic sources, like: DOM Elements: Working with document.getElementById or ViewChild. You are sure it’s an <input>, but TS only knows it is a generic Element. API Data: Data arriving from a legacy service or a third-party library that isn’t typed yet. By using as, you are being proactive and maintaining your type safety rather than just silencing the compiler with any. It is about building components that are not just typed, but truly truly safe. 💡 Beginner Tip: Think of as as an authorization override, but use it sparingly! If you are asserting types everywhere, it is a big warning sign that your underlying data models are incomplete and need more work. Assert only when you have 100% certainty from external knowledge (like knowing you own the DOM)! 👉 Do you use Type Assertions (as) most often for DOM elements, generic API data, or external library data? Let me know👇 Connect /Follow me (SANDEEP KUMAR) so you dont miss the next one #WebDev #CodingTips #NewDeveloper #Beginner #CleanCode #TypeAssertion #DOMManipulation #PredictableState #TypeSafety #Javascript #BTech
To view or add a comment, sign in
-
-
When Signals dropped in Angular, I made a classic mistake.🚦 Started swapping out RxJS in every file I touched. BehaviorSubjects, combineLatest, even some perfectly fine switchMap chains, all gone. Felt great honestly. Even had this quiet confidence that I'd finally "got" Signals, like I'd properly made the switch and left the old way behind. Fast forward two weeks. We had a real-time dashboard where multiple APIs were firing, responses coming back out of order, requests overlapping each other. Classic race condition situation. I tried solving it with Signals. Spent a good chunk of an afternoon on it. A teammate looked at my screen and just said: "why not switchMap?" One operator. Fixed in 10 minutes. I felt a little stupid ngl. That's when I realised I'd been thinking about this wrong the whole time. Signals are genuinely great but they have no concept of time. They won't cancel an in-flight request. They won't debounce a fast-typing user. They won't retry a failed call. RxJS was literally built for that world. The way I think about it now: Is the problem about what value something has? → Signal Is the problem about when or how that value gets here? → RxJS And when you need both, *toSignal()* exists for exactly that. // Signal — owns the state searchQuery =signal(''); // Bridge: Signal → Observable → switchMap → back to Signal results =toSignal(toObservable(this.searchQuery).pipe(debounceTime(300),distinctUntilChanged(),switchMap(query =>this.http.get(`/api/search?q=${query}`))),{ initialValue:[]}); Search input as a Signal, HTTP call as an Observable with switchMap, result piped back via toSignal(). Cancellation just works. No manual subscription cleanup. Honestly took me longer than I'd like to admit to stop forcing one to do the other's job. Curious, did anyone else go through this when Signals first dropped, or was it just me? 📚 Resources: → Angular Signals: angular.dev/guide/signals → RxJS interop (toSignal / toObservable): https://lnkd.in/dmqasMKn → Signals vs RxJS breakdown: https://lnkd.in/dw8CdY9R #Angular #RxJS #Signals #Frontend #WebDevelopment #AngularDeveloper #JavaScript
To view or add a comment, sign in
-
🚀 One RxJS operator that every Angular dev should know — shareReplay() Let me show you a real integration example — step by step. 👇 Imagine you have a User Profile API. Two components — Navbar & Sidebar — both need the user data. Without shareReplay? → 2 HTTP calls 😬 With shareReplay(1)? → 1 HTTP call, shared & cached ✅ --- 📄 Step 1 — user.service.ts import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { shareReplay } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class UserService { // Store the stream — created only once private user$ = this.http .get('/api/user/profile') .pipe(shareReplay(1)); // ← magic here ✨ constructor(private http: HttpClient) {} getUser() { return this.user$; // same stream every time } } --- 📄 Step 2 — navbar.component.ts export class NavbarComponent implements OnInit { userName = ''; constructor(private userService: UserService) {} ngOnInit() { // Subscribes → HTTP call fires (1st time only) this.userService.getUser().subscribe(user => { this.userName = user.name; }); } } --- 📄 Step 3 — sidebar.component.ts export class SidebarComponent implements OnInit { userAvatar = ''; constructor(private userService: UserService) {} ngOnInit() { // Subscribes → gets CACHED result, no new HTTP call! ⚡ this.userService.getUser().subscribe(user => { this.userAvatar = user.avatarUrl; }); } } --- 📄 Step 4 — Even cleaner with async pipe (profile.component.html) <!-- No subscribe/unsubscribe needed! Angular handles it --> <ng-container *ngIf="userService.getUser() | async as user"> <p>Welcome, {{ user.name }} 👋</p> <img [src]="user.avatarUrl" /> </ng-container> --- What just happened? 🤔 🔹 user$ is created once in the service 🔹 Navbar subscribes → HTTP fires, result is cached 🔹 Sidebar subscribes → gets the same cached result instantly 🔹 shareReplay(1) keeps the last 1 value in memory 🔹 Any future subscriber gets it without a new network request That's it. One operator. Clean, efficient, and production-ready. 💪 Save this post for your next Angular project! 🔖 Drop a 🔥 if this helped you! Follow for weekly Angular & RxJS tips. #Angular #RxJS #shareReplay #WebDevelopment #Frontend #JavaScript #TypeScript #Programming
To view or add a comment, sign in
-
𝗧𝘆𝗽𝗲𝘀𝗰𝗿𝗶𝗽𝘁 𝟲.𝟬 𝗙𝗶𝘅𝗲𝘀 𝗥𝗲𝗮𝗹 𝗣𝗮𝗶𝗻 𝗣𝗼𝗶𝗻𝘁𝘀 TypeScript 6.0 solves everyday problems. It makes your code safer and your builds faster. Here is what matters for 2026. **Method syntax now works as you expect.** Before, TypeScript guessed wrong when you used a method instead of an arrow function. Now it checks if "this" is used. Both work the same. ```typescript callIt({ consume(y) { return y.toFixed(); }, // ✅ y is number now }); ``` **Clean imports with #/ prefixes are official.** Stop writing messy relative paths like "../../../utils". Use the Node.js subpath imports feature. ```typescript import * as utils from "#/utils.js"; ``` This matches bundlers like Webpack. Your imports become readable. **Your type unions will now be in a fixed order.** TypeScript 7.0 will check types in parallel. That can randomize union order like `100 | 500`. Use the new `--stableTypeOrdering` flag now to get consistent order. This keeps your compiled files clean. Note: it can slow builds by 25%. **The Temporal API has types.** Say goodbye to JavaScript Date bugs. Use the modern Temporal API for time zones and durations. ```typescript const meeting = Temporal.Instant.from("2026-04-06T10:00:00Z"); const local = meeting.toZonedDateTimeISO("America/Toronto"); ``` **Maps get two helpful new methods.** Replace manual `if (has) get() else set()` logic. ```typescript const timeout = config.getOrInsert("timeout", 5000); const value = config.getOrInsertComputed("key", () => compute()); ``` **Safely build regex from user text.** No more manual escaping bugs. Use the new built-in escape. ```typescript const safe = RegExp.escape(userInput); const regex = new RegExp(safe, "gi"); ``` **Breaking changes you must handle.** Your tsconfig.json defaults have changed. - `strict` is now true. You must list needed types. - `module` defaults to `esnext`. - `target` floats to `es2025`. - `types` defaults to an empty array. **Your migration steps:** Short-term: - Add an explicit `"types": ["node", "jest"]` array in tsconfig. - Set `"rootDir": "./src"` if your source folder is not the project root. - Remove deprecated `baseUrl` path mapping. Use full paths in `paths`. Medium-term: - Move off `target: "es5"`. Use at least ES2015. - Change `--moduleResolution node` to `nodenext` or `bundler`. - Migrate from `amd/umd/system` modules to ESM. Long-term: - Test your build with `--stableTypeOrdering`. - Plan for TypeScript 7.0's parallel checking. - Consider parallel CI builds to offset any speed changes. TypeScript 6.0 is a bridge. It cleans up old defaults and adds features for faster, more predictable future versions. The migration is clear. What feature will you try first? Source: https://lnkd.in/gu9jP4tb
To view or add a comment, sign in
-
TypeScript ships with 20+ utility types. Most developers use 3. Here are the ones I actually reach for in production: ───────────────────────────── Partial<T> When you want optional updates without a new interface. type UpdateUser = Partial<User> // All fields optional — perfect for PATCH requests ───────────────────────────── Pick<T, K> Expose only what the consumer needs. type UserPreview = Pick<User, 'id' | 'name' | 'avatar'> // No accidental exposure of sensitive fields ───────────────────────────── ReturnType<T> Infer the type from a function — not the other way around. type ApiResponse = ReturnType<typeof fetchUser> // Single source of truth: the function itself ───────────────────────────── NonNullable<T> Strip null/undefined before passing down. type SafeId = NonNullable<User['id']> // No optional chaining hell downstream ───────────────────────────── Parameters<T> Extract function arguments as a tuple. type LogArgs = Parameters<typeof logger> // Wrap functions without redefining their signatures ───────────────────────────── Awaited<T> Unwrap Promise types cleanly. type UserData = Awaited<ReturnType<typeof fetchUser>> // No more Promise<Promise<...>> nesting ───────────────────────────── The underrated combo: type SafePartialUpdate<T> = Partial<Pick<T, 'name' | 'email'>> Composing utility types is where TypeScript really pays off. Which utility type do you reach for most? #TypeScript #React #JavaScript #WebDevelopment #SoftwareArchitecture #DeveloperProductivity #NodeJS
To view or add a comment, sign in
-
-
🚀 Day 36 – Starting Node.js | Backend Journey Begins I’m now stepping into the backend world with Node.js to strengthen my journey towards becoming a Full Stack Developer As a frontend developer, I’ve always consumed APIs to display data in the UI. But today, I started understanding what actually happens behind the scenes — how APIs are built, how requests are handled, and how data flows from the frontend to the server and then to the database. --- What is Node.js? Node.js is a JavaScript runtime environment that allows us to execute JavaScript outside the browser. It is built on Chrome’s V8 engine and is designed for building fast and scalable backend applications. One of the most interesting things I learned is that Node.js uses a single-threaded, non-blocking, event-driven architecture, which makes it highly efficient in handling multiple requests without waiting for one task to complete. --- ⚙️ Key Concepts I Learned Today: 🔹 Running JavaScript using Node - Executed ".js" files using "node app.js" - Understood how backend logic runs independently from the browser 🔹 Global Objects - Used "__dirname" to get the current directory path - Used "__filename" to get the current file path - Practiced "console" for logging and debugging 🔹 Module System (CommonJS) - Created reusable functions using "module.exports" - Imported them using "require()" - Understood how backend applications are structured into modules 🔹 Asynchronous Behavior (Introduction) - Used "setTimeout()" to simulate async operations - Observed non-blocking execution - Learned how Node.js can handle multiple operations efficiently --- Key Takeaway: Node.js doesn’t wait for one task to complete before moving to the next. Instead, it handles operations asynchronously, which is very useful in real-time applications like APIs, data fetching, and file handling. --- 💻 What I Practiced: ✔ node -v → Check Node version ✔ npm -v → Check npm version ✔ node app.js → Run application ✔ npm init -y → Initialize project ✔ npm install express → Install package ✔ npx nodemon app.js → Auto-restart server --- Example: console.log("Start"); setTimeout(() => { console.log("Fetching data..."); }, 2000); console.log("End"); 👉 Output: Start End Fetching data... 🌍 Real-Time Understanding: As a frontend developer, when I call an API from Angular, I now understand that Node.js plays a key role in: 👉 Receiving the request 👉 Processing business logic 👉 Interacting with the database (MongoDB) 👉 Sending the response back to the UI This gives me a complete picture of how full-stack applications work. 🚀 What’s Next? ➡️ Deep dive into Async Programming & Event Loop ➡️ Building server using HTTP module ➡️ Learning Express.js ➡️ Creating REST APIs ➡️ Connecting Node.js with MongoDB #NodeJS #MEANStack #Angular #MongoDB #LearningJourney
To view or add a comment, sign in
-
I used to write the same function 3 times for string, number, and object. Then I learned TypeScript Generics. 🔥 Here's everything you need to know (with real React examples): ━━━━━━━━━━━━━━━━━━━━━ 🧩 WHAT IS A GENERIC? ━━━━━━━━━━━━━━━━━━━━━ A Generic is like a label on a box. Whatever type goes IN → the same type comes OUT. Without Generics: ❌ function getItem(item: any) → you lose ALL type safety With Generics: ✅ function getItem<T>(item: T): T → TypeScript helps you everywhere <T> is just a placeholder. It gets replaced with the real type when you call the function. ━━━━━━━━━━━━━━━━━━━━━ ⚙️ THE SYNTAX (memorize this) ━━━━━━━━━━━━━━━━━━━━━ function name<T>(param: T): T { ... } ^^^ ^^^^^^^^ ^^^ | | | | | Return type = T | Input is of type T Declare T here ━━━━━━━━━━━━━━━━━━━━━ ⚛️ GENERICS IN REACT ━━━━━━━━━━━━━━━━━━━━━ 1️⃣ Generic Components Write ONE List component that works with strings, numbers, objects — anything. 2️⃣ Generic useState const [user, setUser] = useState<User | null>(null); 3️⃣ Generic Custom Hooks (most powerful!) useFetch<User>("/api/user") → data is User | null useFetch<Product[]>("/api/products") → data is Product[] | null ONE hook. ANY type. Zero duplication. ━━━━━━━━━━━━━━━━━━━━━ 🔒 CONSTRAINTS (extends) ━━━━━━━━━━━━━━━━━━━━━ Want T to be ANY type BUT must have certain properties? Use extends! function printName<T extends { name: string }>(item: T) → T can be anything, but MUST have a .name property keyof → K must be a real key of T function getValue<T, K extends keyof T>(obj: T, key: K): T[K] → TypeScript protects you from typos in property names! ━━━━━━━━━━━━━━━━━━━━━ 🎯 DEFAULT TYPES ━━━━━━━━━━━━━━━━━━━━━ Just like default function parameters: interface Box<T = string | number> { value: T } → No type given? Falls back to string | number → Type given? Uses that instead ━━━━━━━━━━━━━━━━━━━━━ 📋 QUICK CHEAT SHEET ━━━━━━━━━━━━━━━━━━━━━ <T> → any type <T extends string> → must be string <T extends {name:string}> → must have .name <K extends keyof T> → must be a key of T <T = string> → defaults to string T[K] → value type at key K T[] → array of type T ━━━━━━━━━━━━━━━━━━━━━ ✅ USE Generics when: • Same logic, multiple types • Building reusable components • Writing custom hooks • API response wrappers ❌ SKIP Generics when: • Only one type is ever used • Function is very simple • You end up writing <any> anyway ━━━━━━━━━━━━━━━━━━━━━ Generics felt scary at first. Now I can't imagine writing React without them. If this helped you, repost to help others learn too! ♻️ What TypeScript topic should I break down next? Drop it in the comments 👇 #TypeScript #React #WebDevelopment #JavaScript #Frontend #Programming #100DaysOfCode #CodingTips #SoftwareEngineering #TypeScriptGenerics
To view or add a comment, sign in
-
More from this author
Explore content categories
- Career
- Productivity
- Finance
- Soft Skills & Emotional Intelligence
- Project Management
- Education
- Technology
- Leadership
- Ecommerce
- User Experience
- Recruitment & HR
- Customer Experience
- Real Estate
- Marketing
- Sales
- Retail & Merchandising
- Science
- Supply Chain Management
- Future Of Work
- Consulting
- Writing
- Economics
- Artificial Intelligence
- Employee Experience
- Workplace Trends
- Fundraising
- Networking
- Corporate Social Responsibility
- Negotiation
- Communication
- Engineering
- Hospitality & Tourism
- Business Strategy
- Change Management
- Organizational Culture
- Design
- Innovation
- Event Planning
- Training & Development
Muhammad Mubeen Yasin Do you reuse the same Zod schemas on client and server, or keep them separate? What major drawbacks have you faced?