TypeScript
Completed
-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
122: Avoiding Overuse of any
A few years ago, I was reviewing a PR from a developer who was clearly under a tight deadline. He was integrating a complex third-party analytics SDK that returned a deeply nested, unpredictable JSON object. To move past the compiler errors and get the feature shipped by Friday, he simply typed the entire response as any. It worked perfectly in staging. But three months later, the SDK provider pushed an update that changed a specific property from a string to an array of strings. Because the code used any, TypeScript stayed silent. We didn't find the bug until a production crash report hit my inbox at 2 AM, shouting about .toLowerCase() is not a function.
That's the danger of any. It isn't just a shortcut; it's an explicit instruction to the TypeScript compiler to stop helping you. When you use any, you're essentially telling the language, "I don't know what this is, and I don't want you to check it." In a small project, that's a minor risk. In a large codebase with multiple contributors, it's a landmine.
The Safety Gap Between any and unknown
If you find yourself reaching for any because you truly don't know what the data will look like—perhaps it's coming from an external API or a user-uploaded file—you should almost always use unknown instead. I think of any as "ignore this" and unknown as "I'm not sure yet."
The magic of unknown is that it forces you to perform a type check before you can actually use the value. You can't call a method on an unknown variable or access a property without proving to TypeScript that the variable is actually the type you think it is.
// This is dangerous. No errors here, but it might crash at runtime. function processDataAny(data: any) { console.log(data.toUpperCase()); } // This is safe. TypeScript won't let you call .toUpperCase() until you verify the type. function processDataUnknown(data: unknown) { if (typeof data === "string") { console.log(data.toUpperCase()); // Now this is allowed } }Handling Dynamic Shapes with Record and Generics
Sometimes we use
anybecause we're dealing with an object that has a dynamic set of keys, like a dictionary or a map. Instead of giving up and usingany, use theRecord<K, V>utility type. It allows you to define the type of the keys and the type of the values while still maintaining type safety for the object's structure.For more complex scenarios where the output type depends on the input type, lean on generics. I've seen too many developers use
anyin a function because they want the function to be "flexible." But generics allow that flexibility without throwing away the type information. You're essentially creating a placeholder that gets filled in when the function is actually called.// The "lazy" way: loses all type information function wrapInArrayAny(item: any): any[] { return [item]; } // The "pro" way: preserves the type for the caller function wrapInArray<T>(item: T): T[] { return [item]; } const numbers = wrapInArray(10); // TypeScript knows this is number[] const strings = wrapInArray("Hello"); // TypeScript knows this is string[]The goal isn't to achieve "perfect" types at the cost of your sanity, but to ensure that if something changes in your data layer, the compiler tells you exactly where your code is about to break. If you find yourself typing
any, stop and ask: "Do I actually not know the type, or am I just trying to avoid the effort of defining it?"
📋 Practical Task
Refactoring the Legacy User Profile API
You have been handed a piece of legacy code that handles user profile updates. The previous developer used any everywhere to avoid dealing with the API's dynamic nature. Your task is to make this code type-safe without breaking the functionality.
Requirements:
- Replace the
anytype in theUserProfileinterface with aRecordfor themetadatafield (keys are strings, values are strings). - Change the
apiResponseparameter in thehandleResponsefunction fromanytounknown. - Implement a type guard or a type check inside
handleResponseto ensure the response is an object and contains astatusstring before attempting to log it.
interface UserProfile {
id: string;
username: string;
metadata: any; // TODO: Replace this
}
function handleResponse(apiResponse: any) { // TODO: Replace this
// TODO: Add a check to ensure apiResponse is an object with a 'status' property
console.log("API Status: " + apiResponse.status);
}
const user: UserProfile = {
id: "u123",
username: "dev_mentor",
metadata: { theme: "dark", lang: "en" }
};
handleResponse({ status: "success", code: 200 });There are no comments for now.