Best Practices for Conditional Logic in TypeScript
This article explains the best practices for conditional logic in TypeScript.
YouTube Video
Best Practices for Conditional Logic in TypeScript
Narrowing Types with Type Guards
By leveraging TypeScript's type system and using type guards within if
statements, you can improve type safety and readability.
1function processInput(input: string | number) {
2 if (typeof input === "string") {
3 console.log(`String input: ${input.toUpperCase()}`);
4 } else {
5 console.log(`Number input: ${input.toFixed(2)}`);
6 }
7}
Using typeof
and other type guards ensures that the code within each block is type-safe and contextually relevant.
Avoid Nested if
Statements
Deeply nested if
statements reduce code readability and maintainability. Instead, use early returns, logical operators, or extract functions.
Bad Example:
1function checkUser(user: { age?: number; isAdmin?: boolean }) {
2 if (user.age) {
3 if (user.age > 18) {
4 if (user.isAdmin) {
5 console.log("User is an adult admin.");
6 }
7 }
8 }
9}
Improved Example:
1function checkUser(user: { age?: number; isAdmin?: boolean }) {
2 if (!user.age || user.age <= 18 || !user.isAdmin) {
3 return;
4 }
5 console.log("User is an adult admin.");
6}
This approach flattens the code and makes the logic more understandable.
Utilize Optional Chaining
When checking nested properties, use optional chaining (?.
) to avoid unnecessary if
conditions.
Without Optional Chaining:
1if (user && user.profile && user.profile.email) {
2 console.log(user.profile.email);
3}
Using Optional Chaining:
1if (user?.profile?.email) {
2 console.log(user.profile.email);
3}
This reduces boilerplate code and improves readability. Here, boilerplate code refers to repetitive, standardized code that is frequently required to accomplish specific tasks in programming.
Use Strict Equality Operators
TypeScript supports strict typing, and using strict equality (===
) or strict inequality (!==
) helps prevent unexpected type coercion.
Bad Example:
1if (value == "123") {
2 console.log("Value is 123.");
3}
Improved Example:
1if (value === "123") {
2 console.log("Value is 123.");
3}
Use Enums or Literal Types for Explicit Conditions
Using enums or literal types clarifies conditions and reduces the chances of errors.
1type Status = "success" | "error" | "loading";
2
3function displayMessage(status: Status) {
4 if (status === "success") {
5 console.log("Operation succeeded.");
6 } else if (status === "error") {
7 console.log("Operation failed.");
8 } else {
9 console.log("Loading...");
10 }
11}
By defining expected values, TypeScript can detect types and ensure accuracy.
Combine similar conditions
When multiple conditions share the same logic, consolidate them using logical operators or a switch statement.
Bad Example:
1if (role === "admin") {
2 grantAccess();
3}
4if (role === "superadmin") {
5 grantAccess();
6}
Improved Example:
1if (role === "admin" || role === "superadmin") {
2 grantAccess();
3}
Alternatively, use a switch statement to handle multiple clear branches:
1switch (role) {
2 case "admin":
3 case "superadmin":
4 grantAccess();
5 break;
6 case "user":
7 console.log("Limited access.");
8 break;
9 default:
10 console.log("No access.");
11}
Avoid making conditional expressions complex
Complex conditional expressions in if statements reduce readability. Extract them into meaningful variables or functions.
Bad Example:
1if (user.age > 18 && user.isAdmin && user.permissions.includes("write")) {
2 console.log("User can write.");
3}
Improved Example:
1const isAdultAdminWithWriteAccess =
2 user.age > 18 && user.isAdmin && user.permissions.includes("write");
3
4if (isAdultAdminWithWriteAccess) {
5 console.log("User can write.");
6}
Naming the conditions improves clarity and makes the code self-documenting.
Use ternary operators for simple conditions
For simple conditions, using ternary operators makes the code concise.
Example:
1const message = isLoggedIn ? "Welcome back!" : "Please log in.";
2console.log(message);
However, avoid using ternary operators for complex conditions as it reduces readability.
Replace conditional branches with arrays or maps
When the condition is simply a value mapping, using arrays or maps can improve code readability and maintainability compared to switch
statements or complex if-else
blocks. In TypeScript, you can leverage type information to implement it more safely.
Bad Example:
1function getDayName(day: number): string {
2 switch (day) {
3 case 0:
4 return "Sunday";
5 case 1:
6 return "Monday";
7 case 2:
8 return "Tuesday";
9 case 3:
10 return "Wednesday";
11 case 4:
12 return "Thursday";
13 case 5:
14 return "Friday";
15 case 6:
16 return "Saturday";
17 default:
18 return "Invalid day";
19 }
20}
Improved example: Using an array
1function getDayName(day: number): string {
2 const days = [
3 "Sunday",
4 "Monday",
5 "Tuesday",
6 "Wednesday",
7 "Thursday",
8 "Friday",
9 "Saturday"
10 ];
11 return days[day] ?? "Invalid day";
12}
Improved example: Using a Map
1function getDayName(day: number): string {
2 const dayMap = new Map<number, string>([
3 [0, "Sunday"],
4 [1, "Monday"],
5 [2, "Tuesday"],
6 [3, "Wednesday"],
7 [4, "Thursday"],
8 [5, "Friday"],
9 [6, "Saturday"]
10 ]);
11 return dayMap.get(day) ?? "Invalid day";
12}
Conclusion
By following these best practices, you can make if statements in TypeScript clear, efficient, and maintainable. By using TypeScript features such as type guards, optional chaining, and enums, you can improve code readability and robustness. By following best practices, you can keep conditions concise and clear, resulting in code that is easier to understand and maintain.
You can follow along with the above article using Visual Studio Code on our YouTube channel. Please also check out the YouTube channel.