Strict Null Checks in TypeScript

TypeScript's strict null checks feature is designed to eliminate null and undefined errors, which are common sources of bugs in JavaScript. By enabling strict null checks, TypeScript forces you to handle null and undefined values explicitly, making your code more robust and reliable. This chapter will explain strict null checks in detail, covering the basics, advanced usage, and best practices with plenty of examples.

Understanding Null and Undefined

Null and Undefined in JavaScript

In JavaScript, null and undefined are two distinct types:

  • null is an assignment value that represents the absence of any object value.
  • undefined indicates that a variable has been declared but not assigned a value.
				
					let a = null;
let b;

console.log(a); // null
console.log(b); // undefined

				
			

Explanation:

  • a is explicitly set to null.
  • b is declared but not initialized, so it is undefined.

Problems with Null and Undefined

Without strict null checks, functions and variables might unexpectedly be null or undefined, leading to runtime errors.

				
					function getLength(str: string) {
  return str.length;
}

const result = getLength(null); // Runtime error

				
			

Explanation:

  • Calling getLength(null) results in a runtime error because null has no length property.

Enabling Strict Null Checks

Setting Up Strict Null Checks

To enable strict null checks, you need to set the strictNullChecks option to true in your tsconfig.json file.

				
					{
  "compilerOptions": {
    "strictNullChecks": true
  }
}

				
			

Explanation:

  • This setting ensures that TypeScript will check for null and undefined values, requiring you to handle them explicitly.

Basic Example with Strict Null Check

				
					function getLength(str: string | null): number {
  if (str === null) {
    return 0;
  }
  return str.length;
}

const result = getLength(null); // 0

				
			

Explanation:

  • The str parameter is a union type (string | null), explicitly allowing null.
  • The function checks if str is null and handles it accordingly.

Output:

				
					0

				
			

Type Guards

Using Type Guards

Type guards are a way to narrow down the type within a conditional block.

				
					function printLength(value: string | number | null) {
  if (typeof value === 'string') {
    console.log(value.length);
  } else if (typeof value === 'number') {
    console.log(value.toString().length);
  } else {
    console.log('Value is null');
  }
}

printLength('Hello'); // 5
printLength(12345);   // 5
printLength(null);    // Value is null

				
			

Explanation:

  • The typeof operator checks the type of value, and TypeScript narrows down the type within each conditional block.

Output:

				
					5
5
Value is null

				
			

User-Defined Type Guards

You can create custom type guards to handle complex types.

				
					interface Cat {
  name: string;
  purrs: boolean;
}

interface Dog {
  name: string;
  barks: boolean;
}

function isCat(animal: Cat | Dog): animal is Cat {
  return (animal as Cat).purrs !== undefined;
}

function greetAnimal(animal: Cat | Dog) {
  if (isCat(animal)) {
    console.log(`Hello, ${animal.name}. Do you purr?`);
  } else {
    console.log(`Hello, ${animal.name}. Do you bark?`);
  }
}

greetAnimal({ name: 'Whiskers', purrs: true }); // Hello, Whiskers. Do you purr?
greetAnimal({ name: 'Rover', barks: true });    // Hello, Rover. Do you bark?

				
			

Explanation:

  • isCat is a user-defined type guard that checks if animal is a Cat.
  • TypeScript uses this type guard to narrow down the type within the if block.

Output:

				
					Hello, Whiskers. Do you purr?
Hello, Rover. Do you bark?

				
			

Non-Nullable Types

Non-Nullable Utility Type

TypeScript provides the NonNullable utility type to exclude null and undefined from a type.

				
					type NonNullableString = NonNullable<string | null | undefined>;

function printNonNullable(str: NonNullableString) {
  console.log(str);
}

printNonNullable('Hello'); // Hello
// printNonNullable(null); // Error: Argument of type 'null' is not assignable

				
			

Explanation:

  • NonNullable<string | null | undefined> results in string.
  • This utility type helps ensure that a value is neither null nor undefined.

Output:

				
					Hello

				
			
				
					Hello

				
			

Using Non-Nullable in Functions

				
					function getFirstChar(str: string | null): string {
  return str!.charAt(0);
}

console.log(getFirstChar('TypeScript')); // T
// console.log(getFirstChar(null)); // Runtime error

				
			

Explanation:

  • The ! operator is used to assert that str is non-null.
  • This can be dangerous if null is not handled properly, so use it cautiously.

Output:

				
					T

				
			

Handling Optional Properties

Optional Properties in Interfaces

Interfaces can have optional properties, which might be undefined.

				
					interface User {
  id: number;
  name: string;
  email?: string;
}

function printUser(user: User) {
  console.log(`ID: ${user.id}, Name: ${user.name}`);
  if (user.email) {
    console.log(`Email: ${user.email}`);
  }
}

const user1: User = { id: 1, name: 'Alice' };
const user2: User = { id: 2, name: 'Bob', email: 'bob@example.com' };

printUser(user1);
printUser(user2);

				
			

Explanation:

  • email is an optional property.
  • The function checks if email is present before trying to access it.

Output:

				
					ID: 1, Name: Alice
ID: 2, Name: Bob
Email: bob@example.com

				
			

Using Optional Chaining

Optional chaining (?.) simplifies accessing optional properties.

				
					function printUserWithOptionalChaining(user: User) {
  console.log(`ID: ${user.id}, Name: ${user.name}`);
  console.log(`Email: ${user.email?.toLowerCase() ?? 'No email'}`);
}

printUserWithOptionalChaining(user1);
printUserWithOptionalChaining(user2);

				
			

Explanation:

  • user.email?.toLowerCase() safely accesses email and calls toLowerCase() if email is not undefined.
  • The ?? operator provides a default value if email is undefined.

Output:

				
					ID: 1, Name: Alice
Email: No email
ID: 2, Name: Bob
Email: bob@example.com

				
			

Best Practices

Always Initialize Variables

Always initialize variables to avoid them being undefined.

				
					let name: string = 'John';
let age: number = 30;

console.log(name, age);

				
			

Explanation:

    • Initializing variables ensures they are not undefined

Use Default Parameters

Use default parameters to handle undefined arguments.

				
					function greet(name: string = 'Guest') {
  console.log(`Hello, ${name}`);
}

greet();        // Hello, Guest
greet('Alice'); // Hello, Alice

				
			

Explanation:

  • name defaults to 'Guest' if no argument is provided.

Output:

				
					Hello, Guest
Hello, Alice

				
			

Avoid Using Non-Null Assertion Operator

(!), as it bypasses TypeScript’s null checks and can lead to runtime errors.

				
					function printLengthWithNonNullAssertion(str: string | null) {
  console.log(str!.length);
}

printLengthWithNonNullAssertion('Hello'); // 5
// printLengthWithNonNullAssertion(null); // Runtime error

				
			

Explanation:

  • The ! operator asserts that str is non-null, which can cause errors if str is actually null.

Output:

				
					5

				
			

Running printLengthWithNonNullAssertion(null) will result in a runtime error since null is not handled.

Use Type Guards and Assertions

Use type guards and assertions to safely handle null and undefined values.

				
					function assertIsDefined<T>(value: T): asserts value is NonNullable<T> {
  if (value === undefined || value === null) {
    throw new Error('Value must be defined');
  }
}

function printLengthWithAssertion(str: string | null) {
  assertIsDefined(str);
  console.log(str.length);
}

printLengthWithAssertion('Hello'); // 5
// printLengthWithAssertion(null); // Error: Value must be defined

				
			

Explanation:

  • assertIsDefined is a custom assertion function that throws an error if the value is null or undefined.
  • This ensures that str is non-null before accessing its properties.

Output:

				
					5

				
			
  • Running printLengthWithAssertion(null) will throw an error, preventing a runtime error later.

Strict null checks in TypeScript enhance code reliability by enforcing explicit handling of null and undefined values. By understanding and implementing these checks, you can avoid common pitfalls associated with nullable values and write safer, more maintainable code. This chapter covered the fundamentals of strict null checks, type guards, utility types, and best practices with practical examples. Happy coding !❤️

Table of Contents

Contact here

Copyright © 2025 Diginode

Made with ❤️ in India