16 Bad TypeScript Habits You Need to Break Now
Grace Collins
Solutions Engineer ยท Leapcell

TypeScript Development Pitfall Avoidance Guide: Common Issues and Solutions
In today's front-end development field, TypeScript has already become the standard for project development. It introduces a powerful type system to JavaScript, greatly enhancing the security and maintainability of the code. However, due to the native weakly typed nature of JavaScript, many developers have a misunderstanding of the type system, and it is easy to fall into various misunderstandings when using TypeScript. This article will summarize the most common development pitfalls and, combined with practical cases, help you write better TypeScript code.
I. Issues Related to Type Declaration
1. Abuse of the any
Type
The any
type turns off TypeScript's type checking mechanism, making the type system ineffective. In actual development, try to avoid using any
and instead use unknown
or explicit type definitions.
// Incorrect example: Turns off type checking and is prone to errors at runtime function parseData(data: any) { return data.user.name.toUpperCase(); } parseData(null); // Throws an error at runtime! // Correct example: Use an interface to define the data structure clearly interface User { name: string; } interface Data { user: User; } function parseData(data: Data): string { return data.user.name.toUpperCase(); }
2. Not Declaring the Function Return Type
Although TypeScript has the ability of type inference, in complex logic, explicitly declaring the return type can significantly enhance code readability and type safety, especially for public functions and library functions.
// Incorrect example: The return type is not clear function getUser(id: number) { if (id === 1) return 'admin'; return null; } // Correct example: Explicitly declare the return type function getUser(id: number): string | null { if (id === 1) return 'admin'; return null; }
3. Irregular Definition of interface
and type
interface
and type
are important ways to define types in TypeScript, but using them randomly will make the code difficult to reuse and maintain. It is recommended to use interface
to define the object structure and type
for type combination or the application of utility types.
// Incorrect example: Duplicate definitions lead to conflicts type User = { name: string; }; interface User { age: number; } // Correct example: Use interface uniformly to define the object interface User { name: string; age: number; }
II. Type Usage and Conversion Issues
4. Overusing Type Assertions
Type assertions are used to bypass the compiler's type checking, but overusing them will undermine the security of type inference. Use them only in special scenarios where the type is clearly known, and give priority to type declarations, interfaces, or generics.
// Incorrect example: Abuse of type assertions leads to type insecurity const data = fetchData() as any; const name = (data as { user: { name: string } }).user.name; // Correct example: Use an interface to define the type clearly interface UserData { user: { name: string; }; } const data: UserData = fetchData(); const name = data.user.name;
5. Ignoring the Application of Utility Types
TypeScript provides a rich set of built-in utility types (such as Partial
, Pick
, Omit
, etc.). Using these utility types reasonably can simplify the code and improve reusability.
// Incorrect example: Redefining type fields interface User { id: number; name: string; age: number; } type UserPreview = { id: number; name: string; }; // Correct example: Use the Pick utility type type UserPreview = Pick<User, 'id' | 'name'>;
6. Forcing Assertions When Types Don't Match
Type assertions are only used to tell the compiler the type and will not perform actual type conversion. For scenarios where type conversion is required, use safe type conversion methods.
// Incorrect example: Incorrect use of assertions for type conversion const val = '123' as unknown as number; // Correct example: Use the Number function for type conversion const val = Number('123');
III. Code Structure and Best Practice Issues
7. Not Using Enums to Manage Constants
Magic strings are difficult to maintain and error-prone in the code. Enums should be used to manage constant values uniformly to improve the readability and maintainability of the code.
// Incorrect example: Using magic strings function getRole(role: string) { if (role === 'admin') return 'administration'; } // Correct example: Using enums to manage constants enum Role { Admin = 'admin', User = 'user', } function getRole(role: Role) { if (role === Role.Admin) return 'administration'; }
8. Not Using Generics to Abstract Duplicated Code
When there is duplicate logic in multiple functions or interfaces, use generics for abstraction to enhance the extensibility and reusability of the code.
// Incorrect example: Repeating the implementation of similar functions function wrapString(value: string): string[] { return [value]; } function wrapNumber(value: number): number[] { return [value]; } // Correct example: Using generics to implement general logic function wrap<T>(value: T): T[] { return [value]; }
9. Not Enabling the strict
Mode
The strict
mode is the core of TypeScript's type checking. Turning it off will cause many potential problems to be ignored. It is recommended to enable the strict
mode in tsconfig.json
.
{ "compilerOptions": { "strict": true } }
IV. Runtime and Detail Issues
10. Ignoring IDE Hints
IDEs such as VSCode can highlight potential type issues. Developers should pay attention to these hints and fix the type errors in the code in a timely manner.
// Incorrect example: Ignoring the type error hinted by the IDE const name: string = 123; // Type mismatch, should be fixed
11. Not Using Type Narrowing
Type narrowing allows TypeScript to automatically narrow the variable type range based on conditional judgments. By reasonably using operators such as typeof
, in
, and instanceof
, runtime errors can be avoided.
// Incorrect example: Not handling the null case function printLength(str: string | null) { return str.length; // Throws an error } // Correct example: Using type narrowing function printLength(str: string | null) { if (str) { return str.length; } return 0; }
12. Not Handling null
and undefined
TypeScript does not force the handling of null
and undefined
by default. It is recommended to enable the strictNullChecks
option and explicitly handle null values in the code.
// Incorrect example: Not considering the case where name is undefined function greet(name: string) { return 'Hello ' + name.toUpperCase(); } // Correct example: Using optional parameters and handling null values function greet(name?: string) { return name ? 'Hello ' + name.toUpperCase() : 'Hello'; }
13. Accessing Object Properties Without Null Checks
When accessing nested object properties, be sure to use the optional chaining operator (?.
) or the nullish coalescing operator (??
) for null checks to prevent runtime errors.
// Incorrect example: Not checking if user exists const username = user.profile.name; // Correct example: Using optional chaining and nullish coalescing const username = user?.profile?.name ?? 'Anonymous';
V. Other Common Issues
14. Not Explicitly Specifying Generic Parameters
When using generics, explicitly pass in type parameters to avoid potential problems caused by unclear type inference.
// Incorrect example: The type is not clear const arr = Array(); // The type is any[] // Correct example: Explicitly specify the type const arr: Array<number> = [];
15. Mixing ==
and ===
==
will perform implicit type conversion, which may lead to unexpected results. In TypeScript, always use the strict equality operator ===
for comparison.
// Incorrect example: Using == may cause ambiguity if (value == null) { // May match both undefined and null } // Correct example: Using === for a clear judgment if (value === null) { // Only matches null }
Conclusion
The key to mastering TypeScript lies in a deep understanding of the design concept of the type system and developing a standardized coding habit. By avoiding the above 16 common issues and reasonably using features such as type declarations, utility types, and generics, you will be able to write more secure and maintainable code and fully leverage the advantages of TypeScript.
Leapcell: The Best of Serverless Web Hosting
Finally, I recommend a platform that is most suitable for deploying nodejs services: Leapcell
๐ Build with Your Favorite Language
Develop effortlessly in JavaScript, Python, Go, or Rust.
๐ Deploy Unlimited Projects for Free
Only pay for what you useโno requests, no charges.
โก Pay-as-You-Go, No Hidden Costs
No idle fees, just seamless scalability.
๐ Explore Our Documentation
๐น Follow us on Twitter: @LeapcellHQ