In this article, we’ll explore the Top 10 Mistakes in TypeScript and discuss best practices to avoid them with bad and good examples.
1️⃣ Ignoring Type Annotations and Letting TypeScript Infer Everything 🎯
❌ Mistake: Relying Too Much on Type Inference
TypeScript can infer types, but sometimes it’s better to explicitly define them to avoid unexpected behavior.
function add(a, b) {
return a + b;
}
console.log(add(5, "10")); // ❌ Output: "510" (string concatenation instead of number addition)
✔ Issue: No explicit type annotations, leading to wrong results.
✅ Solution: Use Explicit Type Annotations
function add(a: number, b: number): number {
return a + b;
}
console.log(add(5, 10)); // ✅ Output: 15
✔ Best Practices:
- Use explicit types for function parameters and return types.
- TypeScript does infer types, but don’t over-rely on it.
2️⃣ Using the any
Type Everywhere ❌
❌ Mistake: Overusing any
to Skip Type Checking
let data: any = "Hello";
data = 42; // ❌ No error, but can cause unexpected issues later
✔ Issue: Using any
disables type safety, which defeats the purpose of TypeScript.
✅ Solution: Use Proper Types or Generics
let data: string = "Hello"; // ✅ Now, only strings are allowed
data = 42; // ❌ Error: Type 'number' is not assignable to type 'string'
✔ Best Practices:
- Use specific types (
string
,number
,boolean
) instead ofany
. - Use union types (
string | number
) if a variable can have multiple types.
3️⃣ Not Using Interfaces or Type Aliases for Objects 🏗️
❌ Mistake: Using Inline Object Types
function getUser(user: { name: string; age: number }) {
console.log(`${user.name} is ${user.age} years old.`);
}
✔ Issue: Hard to reuse and scales poorly.
✅ Solution: Use Interfaces for Better Readability
interface User {
name: string;
age: number;
}
function getUser(user: User) {
console.log(`${user.name} is ${user.age} years old.`);
}
✔ Best Practices:
- Use interfaces or type aliases for object shapes.
- Helps in code reuse and maintainability.
4️⃣ Using ==
Instead of ===
for Comparisons 🔍
❌ Mistake: Using ==
Leads to Unexpected Behavior
console.log(0 == "0"); // ❌ true (type coercion)
console.log(false == ""); // ❌ true (confusing behavior)
✔ Issue: ==
allows type coercion, which can lead to unexpected results.
✅ Solution: Always Use ===
for Type-Safe Comparisons
console.log(0 === "0"); // ✅ false
console.log(false === ""); // ✅ false
✔ Best Practices:
- Always use strict equality (
===
) to avoid unintended type coercion.
5️⃣ Not Using Union and Intersection Types ⚡
❌ Mistake: Using any
Instead of Union Types
function logId(id: any) {
console.log(`ID: ${id}`);
}
logId(123); // ✅ Works
logId("ABC"); // ✅ Works
logId(true); // ❌ Unexpected behavior
✔ Issue: Allows unintended types.
✅ Solution: Use Union Types for More Control
function logId(id: string | number) {
console.log(`ID: ${id}`);
}
logId(123); // ✅ Works
logId("ABC"); // ✅ Works
logId(true); // ❌ Error: Argument of type 'boolean' is not assignable
✔ Best Practices:
- Use union types (
|
) when variables can be multiple types. - Use intersection types (
&
) for combining interfaces.
6️⃣ Forgetting readonly
for Immutable Properties 🔒
❌ Mistake: Modifying Object Properties Unintentionally
class User {
name: string;
constructor(name: string) {
this.name = name;
}
}
const user = new User("Alice");
user.name = "Bob"; // ❌ Allowed (even if it shouldn’t change)
✔ Issue: Some properties should not be changed after initialization.
✅ Solution: Use readonly
to Make Properties Immutable
class User {
readonly name: string;
constructor(name: string) {
this.name = name;
}
}
const user = new User("Alice");
user.name = "Bob"; // ❌ Error: Cannot assign to 'name' because it is a read-only property.
✔ Best Practices:
- Use
readonly
for immutable fields. - Helps prevent accidental modification.
7️⃣ Not Handling Optional and Undefined Values Properly ⚠️
❌ Mistake: Accessing Optional Properties Without Checks
interface User {
name: string;
age?: number;
}
const user: User = { name: "Alice" };
console.log(user.age.toFixed(2)); // ❌ Error: 'age' is possibly undefined
✅ Solution: Use Optional Chaining (?.
)
console.log(user.age?.toFixed(2)); // ✅ No error, returns undefined if `age` is missing
✔ Best Practices:
- Use
?.
(optional chaining) for safe property access. - Use
??
(nullish coalescing) to provide default values.
8️⃣ Using let
Instead of const
When Possible 🚀
❌ Mistake: Using let
for Variables That Never Change
let username = "Alice";
username = "Bob"; // ❌ Modifiable variable
✔ Issue: Allows unintended reassignments.
✅ Solution: Use const
for Immutable Variables
const username = "Alice"; // ✅ Now, `username` cannot be changed
✔ Best Practices:
- Use
const
by default. - Use
let
only when reassignment is needed.
9️⃣ Using Loops Instead of Array Methods 🔄
❌ Mistake: Using a for
Loop Instead of map()
const numbers = [1, 2, 3];
const doubled = [];
for (let i = 0; i < numbers.length; i++) {
doubled.push(numbers[i] * 2);
}
console.log(doubled); // ❌ [2, 4, 6]
✔ Issue: Imperative code is longer and harder to read.
✅ Solution: Use map()
for Clean and Concise Code
const doubled = numbers.map(num => num * 2);
console.log(doubled); // ✅ [2, 4, 6]
✔ Best Practices:
- Use array methods (
map
,filter
,reduce
) instead of loops.
🔟 Not Enabling Strict Mode in tsconfig.json
🛠️
❌ Mistake: Leaving TypeScript's Strict Mode Disabled
If strict: false
, TypeScript allows loose type checking, leading to potential runtime errors.
✅ Solution: Enable Strict Mode
✔ Modify tsconfig.json
:
{
"compilerOptions": {
"strict": true
}
}
✔ Best Practices:
- Always enable
strict
mode for better type safety.
🎯 Conclusion
Avoiding these common TypeScript mistakes leads to better code maintainability, performance, and fewer bugs.
Quick Recap
✔ Always use explicit types
✔ Avoid any
(use specific types)
✔ Enable strict mode
✔ Use interfaces for better code structure
✔ Use const over let when possible
Keywords
TypeScript best practices, TypeScript mistakes, TypeScript errors, JavaScript, TypeScript tutorials.
Comments
Post a Comment
Leave Comment