Type operators test, validate, and convert the underlying .NET type of values and objects in memory. Because PowerShell is an object-oriented scripting language built on the .NET Common Language Runtime (CLR), variables and pipeline items carry explicit type definitions. Type operators allow you to verify object inheritance without risking null-reference exceptions and safely coerce data without throwing terminating runtime errors.
They provide a safer, more explicit alternative to direct type casting when the outcome of a conversion isn't guaranteed.
The -is and -isnot Operators
The -is and -isnot operators evaluate whether an object is an instance of a specified .NET type, inherits from a particular base class, or implements a specific interface. They always return a boolean: $true or $false.
Syntax
<object> -is [<TypeAccelerator> or <Full.NET.TypeName>]
<object> -isnot [<TypeAccelerator> or <Full.NET.TypeName>]Basic Type Checking
$count = 42
$count -is [int]
$count -is [string]True
False$name = "Tom"
$name -isnot [int]Truewhere:
[int],[string], etc. are type accelerators, the same bracketed shorthand used when declaring strongly-typed variables.-isnotis simply the negated form of-is.
$NodePort = 8080
$NodeName = "sql-cluster-01"
$Services = @("wuauserv", "bits")
[PSCustomObject]@{
IsPortInt = ($NodePort -is [int])
IsNameString = ($NodeName -is [string])
IsServicesArray = ($Services -is [array])
PortNotString = ($NodePort -isnot [string])
}pwsh ./test-types.ps1IsPortInt IsNameString IsServicesArray PortNotString
--------- ------------ --------------- -------------
True True True TrueInheritance and Interface Verification
Unlike inspecting the string output of .GetType().Name (which only returns the exact leaf class), the -is operator respects .NET type inheritance hierarchies and interfaces:
$NumberList = @(1, 2, 3)
[PSCustomObject]@{
IsArray = ($NumberList -is [System.Array])
IsIEnumerable = ($NumberList -is [System.Collections.IEnumerable])
IsObject = ($NumberList -is [System.Object])
IsSingleNumber = ($NumberList -is [int])
}pwsh ./inheritance-check.ps1IsArray IsIEnumerable IsObject IsSingleNumber
------- ------------- -------- --------------
True True True Falsewhere:
System.Arrayimplements theIEnumerableinterface, so-is [System.Collections.IEnumerable]evaluates to$true.- In .NET, all reference and value types derive from
System.Object, so-is [System.Object]evaluates to$true.
Safe Type Conversion with the -as Operator
In PowerShell, direct type casting using bracket notation (e.g., [int]$val = "abc") throws a terminating exception if the input string or object cannot be converted into the target type.
The -as operator performs a safe type conversion. It attempts to convert the left-hand operand to the specified type:
- If the conversion succeeds, it returns the newly converted object.
- If the conversion fails, it returns
$nullinstead of throwing an error or interrupting script execution.
Basic Example
Example 1:
$value = "100" -as [int]
$value
$value.GetType().Name100
Int32Example 2:
$invalid = "not-a-number" -as [int]
$invalidwhere:
"100" -as [int]succeeds, producing an actualInt32value."not-a-number" -as [int]fails silently, returning$nullinstead of throwing an error.
Direct Casting vs. Safe Conversion (-as)
| Feature | Direct Cast ([Type]$val) |
Safe Conversion ($val -as [Type]) |
|---|---|---|
| On Successful Conversion | Returns converted object | Returns converted object |
| On Conversion Failure | Throws terminating exception | Returns $null |
| Script Execution | Halts (unless trapped in try/catch) |
Continues normally |
| Best Used For | Parameter validation, enforcing strict constraints | Validating arbitrary user input, CSV parsing, soft type checking |
$RawInput = "19200"
$BadInput = "NotANumber"
$Port = $RawInput -as [int]
$FailedPort = $BadInput -as [int]
[PSCustomObject]@{
InputParsed = $RawInput
ResultValue = $Port
UnderlyingType = $Port.GetType().Name
FailedResult = if ($null -eq $FailedPort) { "Parsed as `$null" } else { "Success" }
}pwsh ./compare-casting.ps1InputParsed ResultValue UnderlyingType FailedResult
----------- ----------- -------------- ------------
19200 19200 Int32 Parsed as $null-is vs -as at a Glance
| Operator | Purpose | Returns on Failure |
|---|---|---|
-is |
Checks whether a value already is a given type | $false (never throws) |
-isnot |
Checks whether a value is not a given type | $false (never throws) |
-as |
Converts a value to a given type | $null (never throws) |
Practical Examples
Validating Function Input
function Show-Total {
param($Value)
if ($Value -isnot [int] -and $Value -isnot [double]) {
Write-Warning "Expected a numeric value, got $($Value.GetType().Name)."
return
}
Write-Output "Total: $Value"
}
Show-Total -Value 100
Show-Total -Value "not-a-number"./validate-type.ps1Total: 100
WARNING: Expected a numeric value, got String.Safe Conversion in a Loop
$inputs = @("10", "25", "abc", "40")
foreach ($item in $inputs) {
$number = $item -as [int]
if ($null -eq $number) {
Write-Warning "Skipping invalid value: $item"
continue
}
Write-Output "Valid number: $number"
}./safe-conversion.ps1Valid number: 10
Valid number: 25
WARNING: Skipping invalid value: abc
Valid number: 40Sanitizing Input Data
The following script accepts raw, unvalidated configuration strings (such as those read from a plain text .ini file or an external API) and uses -as, -is, and conditional branching to validate and cast each entry:
$RawConfig = @{
TargetHost = "web-lb-01"
ServicePort = "443"
MaxLoadRatio = "0.85"
ScheduledAt = "2026-01-15 02:00:00"
RetryCount = "InvalidInteger"
}
$SanitizedPort = $RawConfig.ServicePort -as [int]
if ($null -eq $SanitizedPort -or $SanitizedPort -lt 1 -or $SanitizedPort -gt 65535) {
Write-Warning "Invalid port encountered. Reverting to default 80."
$SanitizedPort = 80
}
$SanitizedRatio = $RawConfig.MaxLoadRatio -as [double]
if ($null -eq $SanitizedRatio) {
$SanitizedRatio = 0.50
}
$SanitizedDate = $RawConfig.ScheduledAt -as [datetime]
$SanitizedRetries = $RawConfig.RetryCount -as [int]
if ($null -eq $SanitizedRetries) {
$SanitizedRetries = 3 # Safe default fallback
}
$ValidatedPayload = [PSCustomObject]@{
HostName = $RawConfig.TargetHost
PortNumber = $SanitizedPort
LoadLimit = $SanitizedRatio
ExecutionTime = $SanitizedDate
MaxRetries = $SanitizedRetries
}
$ValidatedPayload | Format-Listpwsh ./sanitize-configuration.ps1HostName : web-lb-01
PortNumber : 443
LoadLimit : 0.85
ExecutionTime : 1/15/2026 2:00:00 AM
MaxRetries : 3And if you add these lines at the end of the script above to verify that the output object properties are strongly typed:
$ValidatedPayload.ExecutionTime -is [datetime]
$ValidatedPayload.PortNumber -is [int]True
TrueBest Practices
- Use
-is/-isnotfor type checking in conditionals to avoid null-reference exceptions. - Use
-asfor safe conversion when input may be invalid; it returns$nullinstead of throwing. - Prefer
-isover.GetType()when the variable might be$null. - Always validate the result of
-aswith a$nullcheck before using the converted value. Remember that-isrespects inheritance: an array is also anIEnumerableand anObject. - Combine
-aswith fallback defaults to sanitize external data safely.