PowerShell Null Coalescing and Null Conditional Operators

PowerShell 7.1 added ??, ??=, and ?. — the powershell null coalescing null conditional operators that eliminate verbose null guards that littered scripts for years. Instead of multi-line if ($x -eq $null) checks before every property access, you can write safe, readable one-liners. This guide covers every operator with practical patterns and the subtle differences from C# equivalents that will bite you if you assume they behave identically.
The ?? Null-Coalescing Operator with Default Values
?? returns the left-hand value if it is not $null, or the right-hand value as a fallback. It is the PowerShell equivalent of C#’s ?? operator — with one key difference: in PowerShell, an empty string '' is not null, so ?? returns the empty string, not the default.
# Basic usage
$name = $null
$displayName = $name ?? "Unknown User"
Write-Host $displayName # Unknown User
# Empty string is NOT null — ?? returns the empty string
$empty = ''
$result = $empty ?? "Default"
Write-Host "'$result'" # '' (empty string, not "Default")
# Nested fallback chain
$config = $null
$envValue = $null
$fallback = "localhost"
$server = $config ?? $envValue ?? $fallback
Write-Host $server # localhost
# Practical: read environment variable with default
$logLevel = $env:LOG_LEVEL ?? 'Information'
The ??= Null-Coalescing Assignment Operator
??= assigns the right-hand value to the variable only if the variable is currently $null. This is an in-place conditional assignment — the variable is only mutated when needed.
# Standard pattern — assign default only if null
$config = $null
$config ??= @{ Timeout = 30; Retries = 3 }
Write-Host "Timeout: $($config.Timeout)" # 30
# If already set, ??= does nothing
$config = @{ Timeout = 60; Retries = 5 }
$config ??= @{ Timeout = 30; Retries = 3 }
Write-Host "Timeout: $($config.Timeout)" # 60 — unchanged
# Common pattern: lazy initialization
function Get-Cache {
param([hashtable] $Cache)
$Cache ??= [System.Collections.Generic.Dictionary[string,object]]::new()
return $Cache
}
# In a function — initialize parameter defaults
function Invoke-Export {
param([string] $Path)
$Path ??= "$env:TEMP\export-$(Get-Date -Format 'yyyyMMdd').csv"
Write-Host "Exporting to: $Path"
}
The ?. Null-Conditional Member Access Operator
?. accesses a property or calls a method only if the object is not $null. If the object is $null, the entire expression short-circuits and returns $null instead of throwing a “Cannot access a member on a null-valued expression” error.
# Without ?. — throws if $user is null
# $user.Address.City # ERROR if $user is $null
# With ?. — returns $null safely
$user = $null
$city = $user?.Address?.City
Write-Host "City: $city" # City: (null, no error)
# Real-world: API response that may be null
$response = Invoke-RestMethod 'https://api.example.com/user/99' -ErrorAction SilentlyContinue
$email = $response?.data?.email ?? 'not provided'
Write-Host "Email: $email"
# Chaining through multiple nullable levels
$settings = Get-Content 'config.json' -Raw -ErrorAction SilentlyContinue |
ConvertFrom-Json -ErrorAction SilentlyContinue
$maxRetries = $settings?.network?.retries ?? 3
Write-Host "Max retries: $maxRetries"
Using ?[] for Null-Safe Array Indexing
?[] is the null-conditional index operator. It returns $null instead of throwing when you try to index into a $null collection.
# Without ?[] — throws NullReferenceException if array is null
# $null[0] # ERROR
# With ?[] — returns null safely
$arr = $null
$first = $arr?[0]
Write-Host "First element: $first" # (null, no error)
# Practical: first result of a nullable query
$users = Get-MgUser -Filter "department eq 'Finance'" -ErrorAction SilentlyContinue
$primaryUser = $users?[0]
Write-Host "Primary finance user: $($primaryUser?.DisplayName ?? 'none found')"
# Combined with ?? for a full safe-access pattern
$items = $null
$label = $items?[0]?.Name ?? 'no items'
Write-Host $label # no items
Combining ?? and ?. in Complex Expressions
The real power comes from composing the operators to replace multi-level null-check boilerplate with a single readable expression.
# Before PS 7.1 — verbose null guards
$dept = $null
if ($null -ne $user -and $null -ne $user.Department -and $user.Department.Name) {
$dept = $user.Department.Name
} else {
$dept = 'Unassigned'
}
# After PS 7.1 — one line
$dept = $user?.Department?.Name ?? 'Unassigned'
# Enriching objects from an API response
$enriched = $apiResponse?.results | ForEach-Object {
[PSCustomObject]@{
Id = $_.id
FullName = "$($_.firstName) $($_.lastName)".Trim()
Manager = $_.manager?.displayName ?? 'No Manager'
Location = $_.officeLocation ?? $_.address?.city ?? 'Unknown'
}
}
Compatibility: What Breaks in PS 5.1
All three operators (??, ??=, ?., ?[]) are unavailable in Windows PowerShell 5.1. Attempting to use them produces parse errors. Write backward-compatible guards when scripts must target both runtimes.
# Check version at the top of scripts that must run on 5.1
if ($PSVersionTable.PSVersion.Major -lt 7) {
Write-Warning "This script requires PowerShell 7.1 or later for null operators."
exit 1
}
# OR write version-compatible helper functions
function Get-ValueOrDefault {
param($Value, $Default)
if ($null -eq $Value) { return $Default }
return $Value
}
# 5.1-compatible equivalent of: $user?.Name ?? 'Unknown'
$name = Get-ValueOrDefault -Value ($user -and $user.Name ? $user.Name : $null) -Default 'Unknown'
Common Errors and Fixes
?? returns $null when the left side is an empty string — different from C# behavior. In C#, ?? only evaluates the right side when the left side is actually null. PowerShell behaves the same way — but PowerShell developers often expect empty strings to fall through to the default. Use -or for falsy checks: ($value -or 'default') treats both $null and empty string as falsy.
?. on a non-nullable .NET value type throws MemberAccessException. ?. cannot protect you from invalid member access on value types like [int] or [bool] that are never actually $null in .NET. It only short-circuits when the object itself is $null. Wrap .NET value-type member access in try/catch when the type is uncertain.
Related Cmdlets / See Also
- PowerShell Null Handling Guide
- PowerShell Ternary Operator Explained
- PowerShell Type Casting and Conversion
Wrapping Up
The null-safe operators in PowerShell 7.1 remove a class of boilerplate that made scripts unnecessarily verbose. Use ?? for defaults, ??= for lazy initialization, and ?. plus ?[] for safe navigation through object graphs from API responses and config files. Just remember: they require PS 7.1+, and empty strings are not null in PowerShell.


