Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The ??= (logical nullish assignment) operator evaluates its right-hand side (RHS) operand and assigns the resulting value to its left-hand side (LHS) operand strictly if, and only if, the LHS operand evaluates to a nullish value (null or undefined).
LHS ??= RHS;
Unlike a standard assignment operator (=), ??= utilizes short-circuit evaluation. If the LHS operand evaluates to any non-nullish value—including falsy values like 0, "" (empty string), NaN, or false—the RHS expression is completely ignored, and the assignment operation is bypassed. Crucially, the ??= operator guarantees that the LHS expression is evaluated exactly once. This makes it semantically distinct from both LHS = LHS ?? RHS and LHS ?? (LHS = RHS):
  1. LHS = LHS ?? RHS always performs an assignment, which can trigger unwanted side effects in property setters even if the value remains unchanged.
  2. LHS ?? (LHS = RHS) evaluates the LHS reference twice if the initial evaluation is nullish.
By evaluating the LHS only once, ??= prevents unintended side effects such as double-invocation of getters, redundant function calls in property access chains, or multiple increments in index expressions.
let i = 0;
const arr: (number | null)[] = [null, 20];

// The LHS (arr[i++]) is evaluated exactly once.
// `i` increments to 1. Because arr[0] is null, 10 is assigned to arr[0].
arr[i++] ??= 10; 

// Conversely, the expanded logical equivalent evaluates the LHS twice if nullish.
// If used instead, `i` would increment twice.
// arr[i++] ?? (arr[i++] = 10); 

Type System Behavior

Under standard strict mode (strictNullChecks: true), TypeScript performs control flow analysis and type narrowing on the ??= operator. After the operation, the type of the LHS is narrowed to exclude null and undefined if the RHS is guaranteed to be non-nullish. TypeScript permits the use of ??= even if the LHS type does not include null or undefined. In such cases, the compiler simply infers that the RHS is unreachable at runtime and leaves the LHS type unchanged, compiling successfully without emitting an error. The following examples demonstrate the assignment mechanics and type narrowing across different types and values:
// Case 1: LHS explicitly includes null
let val1: number | null = null;
val1 ??= 10; 
// Result: val1 is 10. Type of val1 is narrowed to `number`.

// Case 2: LHS explicitly includes undefined
let val2: string | undefined = undefined;
val2 ??= "TypeScript"; 
// Result: val2 is "TypeScript". Type of val2 is narrowed to `string`.

// Case 3: LHS is non-nullish at runtime, but typed to allow null
let val3: number | null = 5;
val3 ??= 100; 
// Result: val3 remains 5. The RHS is ignored.

// Case 4: LHS is falsy, but not nullish
let val4: boolean | undefined = false;
val4 ??= true; 
// Result: val4 remains false.

// Case 5: LHS is never nullish
let val5: number = 5;
val5 ??= 10; 
// Valid TypeScript. Compiles without error. val5 remains 5.

Short-Circuiting and RHS Side Effects

Because of short-circuit evaluation, any function calls or operations on the RHS are deferred and ultimately skipped if the LHS is non-nullish. This guarantees that computationally expensive operations or state-mutating functions are not executed unnecessarily.
function executeSideEffect(): number {
    console.log("RHS evaluated");
    return 10;
}

let existingValue: number | null = 5;

// The function executeSideEffect() is never invoked 
// because existingValue is neither null nor undefined.
existingValue ??= executeSideEffect(); 
Master TypeScript with Deep Grasping Methodology!Learn More