Skip to Content
Course content

90: Understanding strict, strictNullChecks, and noImplicitAny

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I often see developers treat the strict flag in tsconfig.json as a "hard mode" setting—something you turn on once you've "mastered" the language, or something you disable when you're in a rush to ship a feature. The biggest misconception I encounter is the belief that these strict checks are just about adding more boilerplate or satisfying a pedantic compiler, rather than actually preventing production crashes.

"It's just more typing" vs. preventing the billion-dollar mistake

Let's look at a concrete example. Imagine you're building a user profile page. You have a User interface where the bio is optional because not every user writes one.

interface User {
  username: string;
  bio?: string;
}

function getBioLength(user: User) {
  return user.bio.length; 
}

If strictNullChecks is off, TypeScript looks at this and says, "Looks good to me!" It assumes that if you're accessing bio, it's probably there. But the moment a user without a bio hits that function, your app crashes with TypeError: Cannot read property 'length' of undefined. This is exactly what Tony Hoare called the "billion-dollar mistake."

When you turn strictNullChecks on, TypeScript stops being "polite" and starts being helpful. It will red-line user.bio.length and tell you that the object is possibly undefined. Now, you're forced to handle that case—either with an optional chain (user.bio?.length) or a proper if-statement. I'd much rather spend three seconds adding a question mark now than three hours debugging a production outage later.

Stopping the "accidental any" leak

Then we have noImplicitAny. A common frustration I hear is: "I know it's an any, why does TypeScript care if I don't explicitly say so?"

The issue isn't that any exists; the issue is when any sneaks into your codebase without you noticing. Consider this function:

function processOrder(order) {
  console.log(order.id);
}

Without noImplicitAny, TypeScript just gives up on the order parameter and implicitly types it as any. You've now created a hole in your type safety. If you later change the Order object to use order.uuid instead of order.id, TypeScript won't warn you here. You've essentially opted out of the whole point of using TypeScript for this function.

By enabling noImplicitAny, the compiler forces you to be intentional. You either define the type, or—if you truly don't know the type—you explicitly write any. That explicit any acts as a red flag for your future self (and your teammates) that this specific spot is a potential weak point in the system.

The master switch: Why 'strict' is the only flag you need

You might be tempted to just cherry-pick strictNullChecks and noImplicitAny and leave the rest. I strongly advise against this. Setting "strict": true is a master switch that enables a suite of checks, including strictPropertyInitialization (which ensures you don't forget to assign values to class properties in the constructor) and strictFunctionTypes.

I think of strict: true as the "professional standard." When you're working on a team, having a consistent strictness level ensures that everyone is held to the same safety standards. If you start with a loose config and try to tighten it up six months into a project, you'll find yourself staring at 5,000 errors and a weekend of misery. Start strict, stay strict.




📋 Practical Task

Fixing the Fragile Product Catalog

You've inherited a small module for a product catalog. The previous developer had strict mode turned off, and now that you've enabled it in the tsconfig.json, the project is full of errors. Your goal is to fix the type safety issues without using any to bypass the errors.

interface Product {
  id: number;
  name: string;
  description?: string; // Optional
  price: number;
}

function printProductDetails(product: Product) {
  // ERROR: Object is possibly 'undefined'
  console.log("Description: " + product.description.toUpperCase());
}

function applyDiscount(product: Product, discount) {
  // ERROR: Parameter 'discount' implicitly has an 'any' type
  return product.price - discount;
}

class Catalog {
  products: Product[];

  constructor() {
    // ERROR: Properties of class 'Catalog' have no initializer 
    // and are not definitely assigned in the constructor.
  }

  addProduct(p: Product) {
    this.products.push(p);
  }
}

// Test cases
const laptop: Product = { id: 1, name: "Laptop", price: 1200 };
printProductDetails(laptop);
console.log(applyDiscount(laptop, 100));

Your task:

  • Fix the printProductDetails function so it handles the optional description safely.
  • Provide a proper type for the discount parameter in applyDiscount.
  • Fix the Catalog class so the products array is properly initialized.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.