Stop Using Optional Properties in TypeScript: Here's Why Being Explicit Beats Convenience

Stop Using Optional Properties in TypeScript: Here's Why Being Explicit Beats Convenience
Photo by Erik Karits / Unsplash

In TypeScript, optional properties have their place—especially in APIs or when dealing with user input where some properties may legitimately be absent. But for internal application logic, leaning heavily on optional properties can be more harmful than helpful. It’s tempting to reach for prop?: string when you think a property might not always be defined, but this habit can lead to sneaky, silent bugs that are hard to debug and track down. Let's dive into why being explicit about undefined values can make your TypeScript code more predictable, readable, and safer.

The Case Against Optional Properties in Internal Logic

When you declare a property as prop?: string, TypeScript interprets this as prop: string | undefined. This seems convenient, as it allows you to skip initializing the property and later access it directly without much fuss. But here’s the catch: because TypeScript doesn’t require you to handle the potential undefined value explicitly, you’re at risk of running into undefined values in places where you expect a defined one.

This issue is even more noticeable in complex applications. When your app grows, the absence of a property can lead to unexpected bugs that slip through the cracks. Here’s why:

  1. Implicit Undefineds: When you use prop?: string, TypeScript isn’t strict about whether prop is actually defined or not. This leniency means you could forget to set the property, and TypeScript won’t throw an error—it just considers it undefined.
  2. Silent Failures: Without strict handling of potential undefined values, you’re more likely to hit runtime errors. Optional properties are effectively nullable, meaning any code that assumes prop is always defined could break in unexpected ways.

Use prop: string | undefined Instead

Being explicit with prop: string | undefined forces you to handle undefined as a possibility every time you access the property. This approach makes your code more robust by helping you address the edge cases directly. Here’s what you gain by declaring properties this way:

  • Cleaner Code: You’re forced to acknowledge every undefined possibility. This may seem like more work at first, but it prevents assumptions about data that may or may not exist.
  • Improved Readability: Colleagues and future-you will thank you for this. When someone reads prop: string | undefined, they know immediately that prop can be undefined and should be handled as such. It’s explicit and, therefore, more readable.
  • Better Compiler Support: TypeScript’s type checker becomes much more effective when it knows you’re explicitly accounting for undefined. This means better error messages and fewer bugs.

Example Comparison

Here’s a comparison that demonstrates the difference between prop?: string and prop: string | undefined:

// ❌ prop?: string - possible runtime error if accessed without checking
function greet(user?: { name?: string }) {
  console.log(`Hello, ${user.name.toUpperCase()}!`); // Runtime error if name is undefined
}

// ✅ prop: string | undefined - forces you to handle undefined cases
function greet(user: { name: string | undefined }) {
  if (user.name) {
    console.log(`Hello, ${user.name.toUpperCase()}!`);
  } else {
    console.log("Hello, guest!");
  }
}

In the second example, TypeScript will alert you if you try to access user.name without checking if it’s undefined, which helps prevent runtime errors.

Other Tips for Writing Safe TypeScript Code

Beyond avoiding optional properties in internal logic, here are other best practices for writing safer, more predictable TypeScript code:

  1. Leverage Strict Null Checks: Make sure strictNullChecks is enabled in your tsconfig.json. This setting forces you to explicitly handle null and undefined values, ensuring you can’t ignore potential nullish values in your code.
  2. Use NonNullable When Possible: If a property or variable shouldn’t be nullable, use TypeScript’s NonNullable utility type to enforce this. For example: type NonNullableName = NonNullable<string | undefined>;. This helps TypeScript throw errors if you accidentally introduce undefined values where they shouldn’t exist.
  3. Initialize Properties in Constructors: Another way to avoid undefined properties is by initializing them in constructors. For instance, class User { name: string; constructor(name: string) { this.name = name; } }. This ensures name is always defined, and you won’t have to handle undefined cases.
  4. Consider Type Guards: If you find yourself working with values that could be undefined, consider creating type guards to check them. This approach helps to clarify when values are defined, making your code easier to follow.
function isDefined<T>(value: T | undefined): value is T {
  return value !== undefined;
}

Now you can use this guard in conditional statements, making your code safer:

const user = { name: undefined };
if (isDefined(user.name)) {
  console.log(user.name.toUpperCase());
}
  1. Use unknown for Safer APIs: When handling data from APIs or third-party libraries, using unknown instead of any can protect you from unsafe assumptions about data types. This forces you to explicitly define types before using data, which pairs well with the practice of avoiding optional properties.

Finally

In short, avoiding optional properties (prop?: string) in your internal TypeScript code can drastically improve the predictability and robustness of your application. By using prop: string | undefined, you’re forced to handle the undefined case explicitly, reducing the likelihood of silent errors. Pair this with other TypeScript best practices—such as enabling strict null checks, initializing properties in constructors, and using type guards—and you’ll find that your code is cleaner, more readable, and far less prone to unexpected behavior.

Embrace being explicit—it’s the small change that can save you from a lot of debugging headaches down the road.

Support Us