Stop writing the same null check 5 times. Use a discriminated union instead. I spent years writing code like this: if (data && data.user && data.user.id) { ... } Then I started modeling state properly with discriminated unions in TypeScript, and it changed how I structure entire features. Instead of a blob of optional fields, you define each possible state explicitly: type FetchState<T> = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: T } | { status: 'error'; message: string } Now TypeScript narrows the type for you. No more defensive ?. chains. No more "wait, can this be null here?" The shape of your data tells the story. It works especially well in React, where UI state maps directly to these cases. One switch on status and each branch is fully typed. The shift in thinking: stop modeling data as "what fields might exist" and start modeling it as "what states can this be in." What patterns do you reach for first when typing async state in TypeScript? #TypeScript #React #WebDevelopment
Radu Catalin-Andrei’s Post
More Relevant Posts
-
If your TypeScript type has three optional fields that are "never all set at the same time" — that's not a type, that's a verbal agreement. { data?: User; error?: Error; loading?: boolean } Three optional fields allow 8 possible combinations. Only 3 are valid: loading, success, or error. TypeScript cannot catch the other 5 because you never described what valid looks like. // Optional fields — 8 states, 5 are invalid type AsyncState = { data?: User; error?: Error; loading?: boolean; }; // loading + data? Valid TypeScript. Runtime bug. // error + data? Valid TypeScript. Undefined behavior. A discriminated union cuts this to exactly the states you intend: type AsyncState = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: User } | { status: 'error'; error: Error }; Now TypeScript knows: if status === 'success', data exists. In the loading branch, accessing data is a compile error. Every switch is exhaustive-checked automatically. This pattern predates TypeScript. Richard Feldman's 2016 Elm talk "Making Impossible States Impossible" named the principle. XState, Redux Toolkit, and React Query all encode state as discriminated unions internally for exactly this reason. When this doesn't apply: • Simple on/off boolean flags — a single boolean is not an "impossible state" problem • React Query's useQuery already returns a discriminated shape — don't rewrap it • Config objects where fields are genuinely independent of each other The "60% fewer runtime errors" stat that circulates online is unsourced. The real benefit is compile-time exhaustiveness checking — TypeScript tells you which cases you haven't handled before you ship. Are you modeling async state with optional fields, or with unions that make invalid states impossible to represent? #TypeScript #TypeSafety #ReactDevelopment #JavaScript #SoftwareEngineering
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
-
-
TypeScript solution to "At least one of these fields required" 💫 Ever needed a type where multiple base fields exist, but at least one of several specific fields must be present? Here's the pattern and how the union branches work: 𝚝𝚢𝚙𝚎 𝙰𝚝𝙻𝚎𝚊𝚜𝚝𝙾𝚗𝚎 = { [𝙺 𝚒𝚗 𝙺𝚎𝚢𝚜]: 𝙾𝚖𝚒𝚝 & 𝚁𝚎𝚚𝚞𝚒𝚛𝚎𝚍> & 𝙿𝚊𝚛𝚝𝚒𝚊𝚕>> }[𝙺𝚎𝚢𝚜]; The core point: mapped type distributes into a union For 𝙰𝚝𝙻𝚎𝚊𝚜𝚝𝙾𝚗𝚎<{𝚎𝚖𝚊𝚒𝚕?, 𝚙𝚑𝚘𝚗𝚎?, 𝚞𝚜𝚎𝚛𝚗𝚊𝚖𝚎?, 𝚗𝚊𝚖𝚎, 𝚊𝚐𝚎}, "𝚎𝚖𝚊𝚒𝚕" | "𝚙𝚑𝚘𝚗𝚎" | "𝚞𝚜𝚎𝚛𝚗𝚊𝚖𝚎">, TypeScript builds three branches: Branch 1 (email required, phone & username optional): { 𝚗𝚊𝚖𝚎: 𝚜𝚝𝚛𝚒𝚗𝚐; 𝚊𝚐𝚎: 𝚗𝚞𝚖𝚋𝚎𝚛 } // ← 𝙾𝚖𝚒𝚝 (𝚋𝚊𝚜𝚎 𝚏𝚒𝚎𝚕𝚍𝚜) & { 𝚎𝚖𝚊𝚒𝚕: 𝚜𝚝𝚛𝚒𝚗𝚐 } // ← 𝚁𝚎𝚚𝚞𝚒𝚛𝚎𝚍 (𝚌𝚑𝚘𝚜𝚎𝚗 𝚘𝚗𝚎) & { 𝚙𝚑𝚘𝚗𝚎?: 𝚜𝚝𝚛𝚒𝚗𝚐; 𝚞𝚜𝚎𝚛𝚗𝚊𝚖𝚎?: 𝚜𝚝𝚛𝚒𝚗𝚐 } // ← 𝙿𝚊𝚛𝚝𝚒𝚊𝚕 (𝚝𝚑𝚎 𝚘𝚝𝚑𝚎𝚛𝚜) Branch 2 (phone required, email & username optional): { 𝚗𝚊𝚖𝚎: 𝚜𝚝𝚛𝚒𝚗𝚐; 𝚊𝚐𝚎: 𝚗𝚞𝚖𝚋𝚎𝚛 } & { 𝚙𝚑𝚘𝚗𝚎: 𝚜𝚝𝚛𝚒𝚗𝚐 } & { 𝚎𝚖𝚊𝚒𝚕?: 𝚜𝚝𝚛𝚒𝚗𝚐; 𝚞𝚜𝚎𝚛𝚗𝚊𝚖𝚎?: 𝚜𝚝𝚛𝚒𝚗𝚐 } Branch 3 (username required, email & phone optional): { 𝚗𝚊𝚖𝚎: 𝚜𝚝𝚛𝚒𝚗𝚐; 𝚊𝚐𝚎: 𝚗𝚞𝚖𝚋𝚎𝚛 } & { 𝚞𝚜𝚎𝚛𝚗𝚊𝚖𝚎: 𝚜𝚝𝚛𝚒𝚗𝚐 } & { 𝚎𝚖𝚊𝚒𝚕?: 𝚜𝚝𝚛𝚒𝚗𝚐; 𝚙𝚑𝚘𝚗𝚎?: 𝚜𝚝𝚛𝚒𝚗𝚐 } Final type = Branch 1 | Branch 2 | Branch 3 Usage: 𝚝𝚢𝚙𝚎 𝙲𝚘𝚗𝚝𝚊𝚌𝚝 = 𝙰𝚝𝙻𝚎𝚊𝚜𝚝𝙾𝚗𝚎<{ 𝚎𝚖𝚊𝚒𝚕?: 𝚜𝚝𝚛𝚒𝚗𝚐; 𝚙𝚑𝚘𝚗𝚎?: 𝚜𝚝𝚛𝚒𝚗𝚐; 𝚞𝚜𝚎𝚛𝚗𝚊𝚖𝚎?: 𝚜𝚝𝚛𝚒𝚗𝚐; 𝚗𝚊𝚖𝚎: 𝚜𝚝𝚛𝚒𝚗𝚐; 𝚊𝚐𝚎: 𝚗𝚞𝚖𝚋𝚎𝚛; }, "𝚎𝚖𝚊𝚒𝚕" | "𝚙𝚑𝚘𝚗𝚎" | "𝚞𝚜𝚎𝚛𝚗𝚊𝚖𝚎">; // ✅ Branch 1 — has email 𝚌𝚘𝚗𝚜𝚝 𝚌𝟷: 𝙲𝚘𝚗𝚝𝚊𝚌𝚝 = { 𝚎𝚖𝚊𝚒𝚕: "𝚊𝚕𝚎𝚡@𝚎𝚡𝚊𝚖𝚙𝚕𝚎.𝚌𝚘𝚖", 𝚗𝚊𝚖𝚎: "𝙰𝚕𝚎𝚡", 𝚊𝚐𝚎: 𝟸𝟼 }; // ✅ Branch 2 — has phone 𝚌𝚘𝚗𝚜𝚝 𝚌𝟸: 𝙲𝚘𝚗𝚝𝚊𝚌𝚝 = { 𝚙𝚑𝚘𝚗𝚎: "+𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿𝟶", 𝚗𝚊𝚖𝚎: "𝙰𝚕𝚎𝚡", 𝚊𝚐𝚎: 𝟸𝟼 }; // ✅ Branch 3 — has username 𝚌𝚘𝚗𝚜𝚝 𝚌𝟹: 𝙲𝚘𝚗𝚝𝚊𝚌𝚝 = { 𝚞𝚜𝚎𝚛𝚗𝚊𝚖𝚎: "𝚊𝚕𝚎𝚡", 𝚗𝚊𝚖𝚎: "𝙰𝚕𝚎𝚡", 𝚊𝚐𝚎: 𝟸𝟼 }; // ✅ Matches multiple branches — has email AND phone 𝚌𝚘𝚗𝚜𝚝 𝚌𝟺: 𝙲𝚘𝚗𝚝𝚊𝚌𝚝 = { 𝚎𝚖𝚊𝚒𝚕: "𝚊𝚕𝚎𝚡@𝚎𝚡𝚊𝚖𝚙𝚕𝚎.𝚌𝚘𝚖", 𝚙𝚑𝚘𝚗𝚎: "+𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿𝟶", 𝚗𝚊𝚖𝚎: "𝙰𝚕𝚎𝚡", 𝚊𝚐𝚎: 𝟸𝟼 }; // ❌ Matches no branch — missing all three! 𝚌𝚘𝚗𝚜𝚝 𝚋𝚊𝚍: 𝙲𝚘𝚗𝚝𝚊𝚌𝚝 = { 𝚗𝚊𝚖𝚎: "𝙰𝚕𝚎𝚡", 𝚊𝚐𝚎: 𝟸𝟼 }; // ^^^ Error: '{ name: string; age: number; }' is not assignable... The [Keys] distributes over "email" | "phone" | "username", creating one branch per key. Each branch requires its key while keeping the rest optional. Scale to any number of required fields, just add them to the second generic. Clean, reusable, fully type-safe.
To view or add a comment, sign in
-
-
Most React devs handle async state with a handful of booleans. isLoading, isError, data, error. All separate. All synchronized manually. The problem: you can end up in impossible states. isLoading: true AND data present at the same time? Technically possible, logically wrong. A better pattern: discriminated unions in TypeScript. type AsyncState<T> = | { status: "idle" } | { status: "loading" } | { status: "success"; data: T } | { status: "error"; error: string } Now your component switches on state.status. TypeScript narrows the type automatically. No impossible states, no guard clauses scattered everywhere, no "why is data undefined when isLoading is false?" Small shift in thinking, big improvement in code clarity. Once you start modeling state as "what combinations can actually exist," your components get dramatically cleaner. What pattern do you use for async state in React? Still on separate booleans, or have you moved to something like this? #TypeScript #React #WebDevelopment
To view or add a comment, sign in
-
Type errors slip through because strict mode is off and any is everywhere. ────────────────────────────── Partial and Required Utility Types Guide with Examples Learn how to effectively use Partial and Required utility types in TypeScript. This tutorial covers detailed explanations, practical examples, and common pitfalls to help you master these concepts. hashtag#typescript hashtag#utilitytypes hashtag#partial hashtag#required hashtag#programming hashtag#intermediate ────────────────────────────── Core Concept Partial and Required are utility types that were introduced in TypeScript 2.1. They are part of a broader set of utility types that TypeScript provides to manipulate types more effectively. These utilities help developers write more robust and maintainable code by allowing the definition of types that can be modified dynamically. Partial<T> constructs a type with all properties of T set to optional. This is particularly useful in scenarios where not all properties are necessary at all times, such as during updates or optional configurations. Required<T>, on the other hand, constructs a type with all properties of T set to required. This is useful for scenarios where you need to enforce that certain properties are always present, such as when processing form data. Key Rules • Always prefer Partial when updating objects to allow flexibility. • Use Required to enforce strict property requirements during data creation. • Leverage TypeScript’s type inference to reduce redundant type annotations. 💡 Try This interface User { id: number; name: string; ❓ Quick Quiz Q: Is Partial and Required Utility Types different from Type Assertions? A: Yes, Partial and Required are different from type assertions. Type assertions tell the TypeScript compiler to treat a variable as a specific type. In contrast, Partial and Required create new types based on existing ones, modifying their properties. 🔑 Key Takeaway In this tutorial, we covered the usage of Partial and Required utility types in TypeScript. We explored their definitions, use cases, and best practices for implementation. Understanding these utility types helps you create flexible and robust code structures. The next step is to apply these concepts in your applications and explore more advanced TypeScript features. ────────────────────────────── 🔗 Read the full guide with code examples & step-by-step instructions: https://lnkd.in/gbCtW8Pa
To view or add a comment, sign in
-
-
If you have UserTable, ProductTable, and OrderTable that differ only in their data type — you've written the same component three times. TypeScript generics can collapse all three into one: // Three copies, three maintenance points const UserTable = ({ users }: { users: User[] }) => ... const ProductTable = ({ products }: { products: Product[] }) => ... const OrderTable = ({ orders }: { orders: Order[] }) => ... // One generic component function Table<T>({ data, columns }: { data: T[]; columns: ColumnDef<T>[]; }) { return data.map((row, i) => ( <Row key={i} row={row} columns={columns} /> )); } Same component, full type inference. TypeScript infers T from the data prop — you never write the type argument explicitly. If a column accessor references a field that doesn't exist on T, TypeScript catches it at compile time, not at runtime. This is how TanStack Table v8 is built — the core Table<TData> type carries the row data type through columns, rows, cells, and sorting logic. Every accessor is type-checked against TData automatically. One syntax note for TSX files: arrow function generics need a trailing comma to avoid JSX parser ambiguity: const Component = <T,>({ data }: { data: T }) => ... When this doesn't apply: • One-off components that won't be reused — generics add cognitive overhead • Components that differ in behavior, not just type — composition handles that better • Teams new to TypeScript generics — make the duplication visible first, then extract Do you have component files that look suspiciously identical except for their prop types? #TypeScript #ReactDevelopment #JavaScript #FrontendEngineering #Generics
To view or add a comment, sign in
-
-
Frontend developers waste hours on a task that shouldn't exist. You get API data back. It's nested. It's inconsistent. Three different endpoints return IDs as id, userId, and user_id. Before you can build a single component, you're writing transformation logic, manually. Every. Single. Time. So I built apinormaliser.com to kill this entirely. Paste your API responses. The tool: → Normalises keys to camelCase → Flattens and deduplicates nested structures → Merges multiple responses into one clean schema → Generates TypeScript interfaces → Outputs React Query hooks, ready to drop in Zero manual transformation. Zero inconsistency. Based on my own testing, it cuts the data-wrangling phase by 60–70% in typical workflows. That's real time back to your sprint. If you work across multiple APIs or inherited a messy backend, try it free → apinormaliser.com Always looking to make it better. Drop a comment with any features you'd want to see or things you'd improve. All feedback welcome. #Frontend #TypeScript #React #DeveloperTools #OpenToWork
To view or add a comment, sign in
-
I used to think data just moves from backend to frontend. Angular proved me wrong. As a backend developer, I’ve always worked with data — but seeing how directly it connects to the UI changed my perspective completely. Data binding is where everything comes alive. It connects logic with what users actually see and interact with. With one-way data binding, data flows in a single direction — simple and predictable. But two-way data binding takes it further, keeping UI and component in sync in real time. Two-way data binding isn’t unique to Angular — other frameworks support it too — but Angular makes it clean and intuitive using [(ngModel)]. This is where I started seeing the full picture — how backend data turns into real user interaction. #Angular #DataBinding #FrontendLearning #BackendDeveloper #WebDevelopment
To view or add a comment, sign in
-
-
TypeScript Generics with Real API Calls Generics really shine when handling dynamic API responses — one function, multiple data shapes, full type safety. Example: Generic API Fetcher type ApiResponse<T> = { data: T; status: number; }; // Generic fetch function async function fetchData<T>(url: string): Promise<ApiResponse<T>> { const res = await fetch(url); const data = await res.json(); return { data, status: res.status, }; } Usage with Different APIs type User = { id: number; name: string; }; type Post = { id: number; title: string; }; // Users API const userRes = await fetchData<User[]>( "https://lnkd.in/gRsbj6mc" ); // Posts API const postRes = await fetchData<Post[]>( "https://lnkd.in/guysyxTE" ); // Fully typed userRes.data[0].name; postRes.data[0].title; Why This Matters: 1. One reusable API layer 2. Strong typing across endpoints 3. Better DX (autocomplete + error catching) 4. Scales well in React / Next.js apps
To view or add a comment, sign in
-
In this post, I focused on visualizing how data moves within a React application using a Data Flow Diagram (DFD). Understanding data flow allows developers to: • Build more organized and scalable applications • Avoid unnecessary complexity and bugs • Clearly separate logic from UI • Improve maintainability and readability This approach helped me move beyond writing components to truly understanding how data drives the entire application. #React #Frontend #WebDevelopment #JavaScript #SoftwareArchitecture #CleanCode
To view or add a comment, sign in
-
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