Best Practices for Conditional Logic in TypeScript

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.

YouTube Video