PowerShell · lesson 3 of 12
Variables, Arrays, Hashtables, and the $null Trap
PowerShell variables are dynamically typed and forgiving — until a hashtable property inside a string, or a null comparison in the wrong order, quietly does something other than what it looks like.
By SkillByExample · Published
Variables need no type declaration, and that flexibility is exactly where two of PowerShell’s best-known gotchas come from.
Variables have no declared type
$count = 5
$count.GetType().Name
$count = "5"
$count.GetType().Name
Int32
String
Same variable, no keyword, no re-declaration — assigning a new kind of value just changes what
$count holds. This is normal and rarely causes problems on its own; the traps below come
from comparing or interpolating values, not from assigning them.
Arrays
$fruits = "apple", "banana", "cherry"
$fruits[0]
$fruits[-1]
$fruits.Count
apple
cherry
3
Comma-separated values are already an array — -1 indexes from the end, same as Python. The
range operator builds a sequence just as directly:
1..5
1
2
3
4
5
The bug: a property access inside a string
$book = @{ Title = "Dune"; Year = 1965 }
"Title: $book.Title"
Title: System.Collections.Hashtable.Title
Not even close to Dune. Inside a double-quoted string, PowerShell interpolates only the
variable itself — $book — and everything after it, .Title, is left as literal text glued
onto the end. $book on its own becomes its .ToString(), which for a hashtable is just its
type name, and .Title gets appended to that string, not evaluated against the object.
The fix: force evaluation with $(…)
$book = @{ Title = "Dune"; Year = 1965 }
"Title: $($book.Title)"
Title: Dune
$(...) is a subexpression — anything inside it is evaluated first, and that result gets
interpolated. Any property access, method call, or expression inside a string needs this
wrapper; a bare $variable is the only thing simple interpolation understands.
Hashtables
$book = @{ Title = "Dune"; Year = 1965; Author = "Herbert" }
foreach ($key in $book.Keys) { "$key = $($book[$key])" }
Author = Herbert
Title = Dune
Year = 1965
A plain @{ } doesn’t promise to keep the order keys were added in — this one came back
alphabetically here, but that’s incidental, not guaranteed. When the order actually matters,
use [ordered]@{ } instead:
$book = [ordered]@{ Title = "Dune"; Year = 1965; Author = "Herbert" }
foreach ($key in $book.Keys) { "$key = $($book[$key])" }
Title = Dune
Year = 1965
Author = Herbert
The bug: comparing to $null in the wrong order
$x = @(1, $null, 4, $null)
if ($x -eq $null) { "x looks null (WRONG)" } else { "x is not null" }
x looks null (WRONG)
$x is a four-element array — it is very much not null. But $x -eq $null doesn’t test that;
it filters the array element-by-element for values equal to $null, and returns a new
two-element array of the matches. A two-or-more-element array is always truthy in an if,
regardless of what it contains — so this reports “looks null” for exactly the wrong reason.
The fix: put $null on the left
$x = @(1, $null, 4, $null)
if ($null -eq $x) { "x looks null (WRONG)" } else { "x is not null" }
x is not null
$null -eq $x compares $null against $x as a single value on the right, so PowerShell
never gets the chance to distribute the comparison across an array — it returns one real
boolean, correctly, every time. This is why the PowerShell style convention is to always put
$null on the left side of a comparison.
Practice
1. Slice an array to get a middle range of elements.
$scores = 85, 92, 78, 95, 88
$scores[1..3]
92
78
95
1..3 builds the index list 1, 2, 3 first, then indexes with all three at once — the same
range operator from earlier in this lesson, just used as an index instead of a value.
2. Build a connection string from a hashtable's values, using $(...) correctly.
$config = @{ Host = "localhost"; Port = 8080 }
"Connecting to $($config.Host):$($config.Port)"
Connecting to localhost:8080
Two separate $(...) blocks in one string — each is evaluated independently before the whole
string is assembled.
3. Confirm that $null -eq $x stays correct even when every element of $x is null.
$values = @($null, $null)
if ($null -eq $values) { "all null" } else { "not a scalar null" }
not a scalar null
$values is a two-element array, not $null itself, no matter what those two elements are —
$null -eq $values correctly says so regardless of the array’s contents, which is exactly the
property the wrong-order version doesn’t have.
Next: control flow and functions — including how PowerShell functions accept pipeline input.