TypeScript has become the go-to language for building scalable and robust applications, especially in the frontend world. But just because you’re using TypeScript doesn’t mean you’re immune to writing bad code. In fact, many developers unknowingly develop poor habits that undermine the very purpose of using a typed language.
In this article, we’ll explore 16 common TypeScript anti-patterns that are holding back your codebase and how you can break free from them.
1. ❌ Overusing any
Instead of Proper Types
The most notorious of all TypeScript sins is using any
. While it may seem like a quick fix when you’re unsure about a type, it completely disables type checking for that variable. This defeats the whole purpose of using TypeScript.
✅ Fix: Use explicit types or unknown
if you must defer type resolution.
2. ❌ Misusing Type Assertions (as
)
Using as Type
or angle-bracket syntax to force types can lead to runtime errors. If you find yourself asserting types often, it’s a sign your types aren’t correctly modeled.
✅ Fix: Prefer type guards and narrowing over assertions.
3. ❌ Overuse of Optional Properties (?
)
Defining too many optional properties (prop?: string
) can make your types ambiguous and harder to reason about.
✅ Fix: Use unions like string | null
or create separate interfaces for different states.
4. ❌ Ignoring Compiler Errors
Ignoring or suppressing compiler warnings with @ts-ignore
or turning off strict mode is dangerous. It hides real issues in your code.
✅ Fix: Treat warnings as errors. Keep strict mode on and address each issue.
5. ❌ Using Function
as a Type
Writing (arg) => void
as a function type might be convenient, but it lacks specificity and can cause bugs.
✅ Fix: Define explicit function signatures: (input: string): boolean
.
6. ❌ Not Annotating Return Types
Letting TypeScript infer return types might seem harmless, but it can hide bugs and complicate refactoring.
✅ Fix: Always annotate return types unless they’re trivial (e.g., simple getters).
7. ❌ Mutating State Without Immutability
Directly mutating objects or arrays can introduce subtle bugs, especially in frameworks like React where state changes need to be tracked.
✅ Fix: Use immutability patterns like spread operators or libraries like Immer.
8. ❌ Using Enums When Union Types Are Better
Enums are useful, but string literals ("loading" | "success"
) offer better type inference and are easier to serialize.
✅ Fix: Prefer union types for statuses and flags.
9. ❌ Too Many Nested Object Types
Deeply nested object structures can become hard to read and maintain.
✅ Fix: Flatten complex types or extract inner types into their own interfaces.
10. ❌ Assuming JSON Parsing Returns Correct Types
Parsing JSON (JSON.parse()
) gives you any
, not a type-safe value.
✅ Fix: Always validate parsed data using tools like Zod or Yup before assigning types.
11. ❌ Using Classes Unnecessarily
While TypeScript supports OOP, functional programming styles are often simpler and more testable.
✅ Fix: Favor functions and pure logic over class-based state management unless needed.
12. ❌ Poorly Designed Generic Types
Creating overly complex or vague generic types makes code less readable and harder to use.
✅ Fix: Design generics with clarity and constraints in mind.
13. ❌ Not Using Discriminated Unions
Discriminated unions are powerful for modeling state, especially in UI logic.
✅ Fix: Use a literal field (like type: 'success'
) to distinguish between interface variants.
14. ❌ Using Object.keys()
Without Type Safety
Object.keys()
returns an array of strings, which can cause type mismatches.
✅ Fix: Use keyof typeof obj
to ensure keys match known types.
15. ❌ Not Handling All Cases in Switch Statements
If you’re not exhaustive in switch statements or conditional checks, you may miss edge cases.
✅ Fix: Use never
to catch unhandled cases during development.
16. ❌ Reinventing Utility Types
TypeScript provides helpful utility types like Partial<T>
, Required<T>
, and Record<K,T>
.
✅ Fix: Use built-in types instead of writing custom ones unless necessary.
🧩 Conclusion: Clean Up Your TypeScript Act
TypeScript is only as good as the habits you build around it. By identifying and breaking these 16 bad habits, you’ll write safer, more predictable, and more maintainable code.
Whether you’re working on a small personal project or a large enterprise application, refining your TypeScript practices will pay dividends in readability, bug reduction, and developer experience.
Start auditing your code today—your future self (and teammates) will thank you!
💡 Pro Tip:
Use tools like ESLint, Prettier, and TypeScript’s own compiler options to enforce good practices across your team.