TypeScript null and undefined
In TypeScript, null and undefined have special handling.
EnablingstrictNullChecksAfter that, these values need to be explicitly handled, which helps write safer code.
SVG diagram: null and undefined handling
Why handle null and undefined
In JavaScript, null and undefined are common sources of errors, and many runtime errors are related to them.
TypeScript's strictNullChecks option requires developers to explicitly handle values that may be null.
Although this increases coding effort, it can greatly reduce runtime errors caused by null values and improve code reliability.
Concept explanation:
nullRepresents "empty value", i.e., there is no value here;undefinedRepresents "undefined", i.e., it has not been assigned a value yet.
null and undefined basics
null represents "empty value", undefined represents "undefined". Both are independent types in TypeScript.
Example
// Can only be assigned null
var empty: null = null;
// Declare a variable of type undefined
// Can only be assigned undefined
var notDefined: undefined = undefined;
console.log("null: " + empty);
console.log("undefined: " + notDefined);
Output:
null: null undefined: undefined
Explanation:Without enabling strictNullChecks, these types can be assigned to each other. After enabling it, explicit declaration is required.
Union types handling null
After enabling strictNullChecks, you need to use union types to explicitly declare values that may be null.
Example
// Explicitly declare that the value may be null
var name: string | null = "Alice";
name = null; // Correct: can be assigned null
// Accessing a value that may be null requires checking first
// Function parameter may be string or null
function getLength(str: string | null): number {
// Use a conditional check to see if it is null
if (str === null) {
return 0; // Return 0 when null
}
// TypeScript will infer that str is not null
return str.length;
}
console.log("Length: " + getLength("hello"));
console.log("Length: " + getLength(null));
Output:
长度: 5 长度: 0
Best practices:Use type guards (if checks) to narrow types and let TypeScript know the specific type.
Optional parameters and properties
Optional parameters and optional properties automatically include undefined; there is no need to explicitly declare null.
Example
// Parameter may be string or undefined
function greet(name?: string): string {
// Check if it is undefined
if (name === undefined) {
return "Hello, stranger!";
}
return "Hello, " + name;
}
console.log(greet("Alice"));
console.log(greet());
// Optional property: marked with ?
// The age property is optional and may not exist
interface User {
name: string;
age?: number; // Optional property
}
var user: User = { name: "Bob" };
console.log("User: " + JSON.stringify(user));
Output:
Hello, Alice!
Hello, stranger!
User: {"name":"Bob"}
Explanation:Optional parameters
name?: stringequivalent tostring | undefined。
Non-null assertion operator
Using!Tells the compiler that a value is not null or undefined. This should be used with caution.
Example
// Non-null assertion: tells the compiler that str is not null
// This is a dangerous approach; if str is actually null, it will cause an error
return str!.length;
}
console.log("Length: " + getLength("hello"));
Warning:The non-null assertion bypasses TypeScript's type checking. If the value is actually null, it will still cause a runtime error. Avoid using it when possible.
Nullish coalescing operator
Using??Provides a default value; the default value is used only when the value is null or undefined.
Example
var name: string | null = null;
// ?? operator: uses the right side value when the left side is null/undefined
// If name is null, use "Guest"
var displayName = name ?? "Guest";
console.log("Display name: " + displayName);
// Comparing with the || operator (which treats 0 and empty string as falsy)
var num: number | null = 0;
var result1 = num ?? 100; // 0 (correct: 0 is not null/undefined)
var result2 = num || 100; // 100 (incorrect: 0 is treated as falsy)
console.log("?? result: " + result1);
console.log("|| result: " + result2);
Output:
显示名称: Guest ?? 结果: 0 || 结果: 100
Recommendation:Prefer using
??rather than||, because??It only handles null and undefined and does not incorrectly treat 0 or an empty string as falsy.
Optional chaining
Using?.Safely access nested properties that may not exist, avoiding null checks during multiple levels of nesting.
Example
interface Person {
name: string;
address?: {
city: string;
};
}
// Create a person object with no address
var person: Person = { name: "Alice" };
// Optional chaining: safely access properties that may not exist
// If address does not exist, return undefined instead of throwing an error
var city = person.address?.city;
// Combined with the nullish coalescing operator
console.log("City: " + city);
console.log("City: " + (person.address?.city ?? "Unknown"));
Output:
City: undefined City: 未知
Best practice:Optional chaining
?.is the best way to handle nested optional properties, combined with??it can provide a fallback default value.
Notes
- Strict mode:It is recommended to enable strictNullChecks for better type safety
- Avoid assertions:Try to avoid using non-null assertions
! - ?? vs ||:Prefer using
??to avoid misjudging 0 and empty strings - Optional chaining:Use when handling nested optional properties
?.
Best practice:Use type guards, optional chaining, and the nullish coalescing operator to handle null and undefined, making code safer and more readable.
Summary
Correctly handling null and undefined is key to writing safe TypeScript code.
- Strict mode:After enabling strictNullChecks, explicit handling is required
- Union types:Use
string | nullto declare - Optional parameters/properties:Automatically include undefined
- Non-null assertion:Use
!(Use with caution) - Nullish coalescing:Use
??Provide default value - Optional chaining:Use
?.Safe access
Other extensionsRecommendation:Develop the habit of handling null values, using optional chaining and the nullish coalescing operator to make code more robust.