Introduction: Why TypeScript Bites Beginners, or How Not to Fail Your First Project
\n\nHave you ever felt that TypeScript is like a strict teacher who gives bad grades for every little thing? If yes — welcome to the club. Every second beginner, when switching from JavaScript to TypeScript, makes the same mistakes. And that's normal: TypeScript is indeed more complex, it requires discipline and an understanding of types.
\n\nBut there is good news: most mistakes when learning TypeScript can be predicted and prevented. In this article, I've gathered the 7 most common pitfalls that beginner developers step on. We'll break down each mistake with code examples, explain why it's bad, and show how to do it right.
\n\nIf you've just started learning TypeScript or have already gotten a few bumps — this article will save you hours of debugging and nerves. Let's go!
\n\nMistake #1: Uncontrolled Use of any (The Source of All Evils)
\n\nWhat Beginners Do
\nThe most common mistake among those learning TypeScript is writing any everywhere. It seems: "So what if I didn't specify the type, the code works!"
// ❌ Bad: any kills all the benefits of TypeScript\nfunction processData(data: any) {\n console.log(data.name.toUpperCase());\n return data.price * 2;\n}\n\nconst result = processData({ name: "laptop", price: 500 });\n// It works... until you pass null\nprocessData(null); // Runtime error!\n\nWhy This Is a Mistake
\nany is a black hole in TypeScript. When you write any, you tell the compiler: "Step aside, I'll handle it myself." And it steps aside. You lose autocompletion, type checking, and protection against silly mistakes. TypeScript turns back into JavaScript — with all its shortcomings.
How to Fix It
\nUse specific types. If the type is unknown in advance — unknown is your friend. It will force you to check the data before using it.
// ✅ Good: specific types and unknown\nfunction processData(data: { name: string; price: number }) {\n console.log(data.name.toUpperCase());\n return data.price * 2;\n}\n\n// Or if the type is truly unknown:\nfunction safeProcess(data: unknown) {\n if (typeof data === 'object' && data !== null && 'name' in data) {\n // Now TypeScript knows that data is an object with a name field\n console.log((data as { name: string }).name);\n }\n}\n\nTip: Enable the no-explicit-any rule in ESLint — it will prevent you from using any without a good reason.
Mistake #2: Ignoring Strict Mode (or "Why Everything Works, But Not As Expected")
\n\nWhat Beginners Do
\nMany beginner developers, when learning TypeScript, disable strict mode in tsconfig.json or don't enable it at all. They think, "It's fine, fewer compilation errors."
// ❌ Bad: strict: false\n// tsconfig.json\n{\n "compilerOptions": {\n "strict": false, // Mistake!\n "noImplicitAny": false,\n "strictNullChecks": false\n }\n}\n\n// Now this code will compile without errors:\nfunction greet(name: string | null) {\n console.log(name.toUpperCase()); // Potential null!\n}\ngreet(null); // Runtime error: Cannot read properties of null\n\nWhy This Is a Mistake
\nWithout strict mode, TypeScript loses half its power. You don't get warnings about potential null and undefined, you don't check argument types — and end up catching bugs in production.
How to Fix It
\nAlways enable strict: true. This activates all strict checks at once.
// ✅ Good: strict: true\n{\n "compilerOptions": {\n "strict": true,\n "target": "ES2020",\n "module": "ESNext"\n }\n}\n\n// Now TypeScript will force you to handle null:\nfunction g