Variables and Data Types

Reviewed & published by Brayan K

By the end of this lesson you'll be able to store text, numbers, true/false values, arrays, and hashtables in PowerShell variables, convert between types with casting, and build clean output with string interpolation and here-strings — the foundation of every script you'll write.

Part of the free PowerShell course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn

1. Declaring Variables & Common Types

To make a variable, pick a name starting with $ and assign a value with =: $name = "Alice". You never declare the type — PowerShell is dynamically typed, so it figures the type out from the value. The everyday types are String (text), Int32 (whole numbers), Double (decimals) and Boolean ($true / $false). There's also $null, which means "no value at all". Read this worked example, run it, then you'll write your own.

# A variable is a named box that holds a value. In PowerShell every
# variable name starts with a $ sign — the $ is part of the name.
$name    = "Alice"        # text  -> String  (use quotes)
$age     = 30             # whole number -> Int32
$price   = 19.99          # decimal number -> Double
$isAdmin = $true          # true/false -> Boolean ($true or $false)
$nothing = $null          # "no value at all" -> Null

# Every value is a .NET object, so you can ask it for its type name.
Write-Output "name  is a $($name.GetType().Name)"
Write-Output "age   is an $($age.GetType().Name)"
Write-Output "price is a $($price.GetType().Name)"
Write-Output "admin is a $($isAdmin.GetType().Name)"

# $null is genuinely empty. Comparing to it is how you test "is it set?".
Write-Output "nothing is null: $($null -eq $nothing)"

2. Strings: Interpolation, Quotes & Here-Strings

The quote you choose changes what happens. Double quotes expand — a $variable inside the text is replaced by its value, and $(...) runs a small expression and drops the result in. Single quotes are literal — what you type is exactly what you get, so '$name' stays as the text $name. For multi-line text, a here-string (@" ... "@) keeps your layout intact, and the double-quoted form still expands variables inside it.

$user = "Sam"
$count = 3

# Double quotes EXPAND: variables and $(...) subexpressions are replaced.
Write-Output "Hello, $user!"              # Hello, Sam!
Write-Output "You have $count messages"   # You have 3 messages
Write-Output "2 + 2 = $(2 + 2)"           # 2 + 2 = 4   (subexpression)

# Single quotes are LITERAL: nothing is expanded, what you see is what you get.
Write-Output 'Raw text: $user is not expanded'   # Raw text: $user is not expanded

# A here-string holds multi-line text exactly as written.
# Double-quoted @" ... "@ still expands variables inside it.
$report = @"
User:     $user
Messages: $count
Status:   OK
"@
Write-Output $report

Your turn. The script below is almost complete — fill in the three blanks marked ___ using the hints in the comments, then run it.

# 🎯 YOUR TURN — replace each ___ then run the script.

# 1) Make a string called $city set to a city name
$city = ___          # 👉 text in "double quotes", e.g. "London"

# 2) Make an int called $year set to the current year
$year = ___          # 👉 a whole number, e.g. 2026

# 3) Make a bool called $isOpen set to true
$isOpen = ___        # 👉 use $true (not "true")

# These already work once your variables exist (note: double quotes expand):
Write-Output "In $year, the office in $city is open: $isOpen"

# ✅ Expected output (example):
#    In 2026, the office in London is open: True

3. Collections: Arrays & Hashtables

When you need more than one value, reach for a collection. An array holds values in order — build it with @(...) and read items by position, where [0] is the first and [-1] is the last. A hashtable holds key = value pairs (a lookup table) — build it with @{ ... } and read a value by its key with $h.Key or $h['Key']. Inside a string, wrap property access in $(...) so it's evaluated.

# Arrays hold many values in order. Build one with @( ).
$fruits = @("Apple", "Banana", "Cherry")
Write-Output "First: $($fruits[0])"       # index 0 is the first item
Write-Output "Last:  $($fruits[-1])"      # -1 counts from the end
Write-Output "Count: $($fruits.Count)"    # how many items

# += appends — but it builds a brand-new array each time (slow in big loops).
$fruits += "Date"
Write-Output "Joined: $($fruits -join ', ')"

# Hashtables hold key -> value pairs (a lookup table). Build one with @{ }.
$server = @{
    Name   = "WebServer01"
    IP     = "192.168.1.10"
    Status = "Running"
}
Write-Output "Name: $($server.Name)"      # dot access by key
Write-Output "IP:   $($server['IP'])"     # or bracket access by key

4. Type Casting

Sometimes a value is the wrong type — most often you have text that needs to be a number. Put the target type in square brackets in front of the value to cast it: [int]$typed, [double]$priceText, [string]$age. This matters because PowerShell decides what + does from the left-hand value: a string on the left glues text together, while a number on the left adds. Cast first and the maths behaves.

# Text typed by a user is always a String — even if it looks like a number.
$typed = "42"
Write-Output "Without casting: $($typed + 8)"   # "428" — string glued together!

# Cast with [int] to force a real number, then maths works.
$number = [int]$typed
Write-Output "With [int] cast: $($number + 8)"  # 50

# Cast the other way too: [string], [double], [bool] all work.
$rounded = [int]3.9        # 4  -> [int] ROUNDS to nearest, it does NOT truncate
Write-Output "[int]3.9 = $rounded"
# Surprise: exact .5 ties round to the EVEN number (banker's rounding).
Write-Output "[int]2.5 = $([int]2.5)"   # 2  (not 3!)

# You can pin a variable's type by casting on the left-hand side.
[int]$score = "100"
Write-Output "score is now a $($score.GetType().Name) = $score"

Now you try. Input from a user always arrives as text, so before you can do maths you must cast it. Fill in the two blanks:

# 🎯 YOUR TURN — input always arrives as TEXT, so cast before doing maths.
$quantityText = "3"
$priceText    = "4.50"

# 1) Cast $quantityText to a whole number
$quantity = ___          # 👉 use [int]$quantityText

# 2) Cast $priceText to a decimal number
$unitPrice = ___         # 👉 use [double]$priceText

$total = $quantity * $unitPrice
Write-Output "$quantity items at $unitPrice each, total $total"

# ✅ Expected output:
#    3 items at 4.5 each, total 13.5

Pro Tip

For large collections that grow in a loop, avoid $arr += item — it copies the whole array every time, which is O(n). Use a typed list instead: $list = [System.Collections.Generic.List[string]]::new() and $list.Add("x"), which is O(1). For ordered key/value data where insertion order matters, use [ordered]@{ ... } — a plain hashtable does not guarantee order.

Common Errors (and the fix)

📋 Quick Reference

TaskSyntaxResult
String$name = "Alice"Alice
Integer$count = 4242
Boolean$ok = $trueTrue
Array$a = @(1,2,3)1 2 3
Hashtable$h = @{A=1}$h.A → 1
Interpolate"Hi $name"Hi Alice
Subexpression"$($h.A + 1)"2
Cast to int[int]"42"42
Null test$null -eq $xTrue / False

Mini-Challenge: Server Status Report

No blanks this time — just a brief and an outline to keep you on track. Build it, run it, and check your output against the example in the comments. This combines a hashtable, an array, casting a bool, and a here-string — exactly the kind of small script real automation is made of.

# 🎯 MINI-CHALLENGE: Server status report
# 1. Make a hashtable $server with keys: Name, IP, and Online (a bool).
# 2. Make an array $services with 3 service names, e.g. "DNS","Web","SQL".
# 3. Use a double-quoted here-string (@" ... "@) to print a tidy report
#    that shows the name, IP, whether it's online, the service count,
#    and the services joined with ", " (use $($services -join ', ')).
#
# ✅ Example output:
#    Server:   WebServer01
#    IP:       192.168.1.10
#    Online:   True
#    Services: 3 (DNS, Web, SQL)

# your code here

🎉 Lesson Complete!

Practice quiz

What character must every PowerShell variable name start with?

  • $
  • @
  • #
  • %

Answer: $. Every variable name begins with $, for example $name or $count.

What type is the value 19.99 in PowerShell?

  • Int32
  • String
  • Boolean
  • Double

Answer: Double. A decimal number like 19.99 is a Double.

How do double-quoted strings differ from single-quoted ones?

  • They are slower
  • They cannot span lines
  • They expand variables and $() subexpressions
  • They must be empty

Answer: They expand variables and $() subexpressions. Double quotes expand variables and subexpressions; single quotes are literal.

What does 'Raw $user' (single quotes) print?

  • Raw Sam
  • An error
  • Raw $user
  • Raw

Answer: Raw $user. Single quotes are literal, so $user is not expanded.

How do you build an array in PowerShell?

  • [1,2,3]
  • {1,2,3}
  • (1;2;3)
  • @(1, 2, 3)

Answer: @(1, 2, 3). Arrays are built with @( ), e.g. @(1, 2, 3).

Which index gets the LAST item of an array $a?

  • $a[-1]
  • $a[end]
  • $a[last]
  • $a[0]

Answer: $a[-1]. Negative indexing counts from the end, so $a[-1] is the last item.

What syntax builds a hashtable (key/value pairs)?

  • @( )
  • [ ]
  • @{ }
  • < >

Answer: @{ }. Hashtables use @{ }, e.g. @{ Name = 'Web'; IP = '10.0.0.1' }.

Why does the string "42" plus 8 give "428"?

  • 8 is invalid
  • The left value is a string, so + concatenates
  • PowerShell rounds up
  • Integers cannot be added

Answer: The left value is a string, so + concatenates. When the left operand is a string, + glues text together instead of adding.

How do you cast the text "42" to a whole number?

  • (int)"42"
  • [int]"42"
  • int("42")
  • "42".toInt()

Answer: [int]"42". Use the bracketed type cast [int]"42" to get a real Int32.

What is the recommended way to test whether $x is null?

  • $x -eq $null
  • $x == null
  • isnull $x
  • $null -eq $x

Answer: $null -eq $x. Put $null on the left ($null -eq $x) to avoid array-comparison surprises.

Continue this course

Frequently asked questions

Why does my variable print as literal $name instead of the value?

You wrapped the text in single quotes. Single-quoted strings are literal in PowerShell — nothing inside them is expanded. Use double quotes ("Hello $name") whenever you want a variable's value substituted in.

How do I put an object's property inside a string, like $server.Name?

Wrap it in a subexpression: $(...). Inside double quotes, "$server.Name" prints the whole object followed by the literal .Name, because PowerShell only expands the bare variable. Write "$($server.Name)" so the .Name part is evaluated first.

Why did $typed + 8 give me 428 instead of 50?

$typed held the string "42", and with a string on the left the + operator concatenates, gluing "8" on the end. Cast it to a number first: [int]$typed + 8 gives 50. This automatic-coercion-by-left-operand surprise is the classic PowerShell trap.

What's the difference between $null, "" and 0?

$null means 'no value assigned at all'. An empty string "" is a real String of length 0, and 0 is a real number. They are different things — test for unset with the idiom $null -eq $yourVar (put $null on the left).

When should I use a here-string instead of normal quotes?

Reach for a here-string (@" ... "@ or @' ... '@) when your text spans multiple lines or contains lots of quotes you'd otherwise have to escape — banners, config blocks, SQL, HTML. The double-quoted form still expands variables; the single-quoted form keeps everything literal.