Edit

Share via


about_Type_Conversion

Short description

PowerShell has a flexible type system that makes it easier to use. However, you must understand how it works to avoid unexpected results.

Long description

By default, PowerShell variables aren't type-constrained. You can create a variable containing an instance of one type and later assign values of any other type. Also, PowerShell automatically converts values to other types, both explicitly and implicitly. While implicit type conversion can be helpful, there are pitfalls, especially for users more familiar with languages that have stricter type handling.

Type-constrained variables and explicit types conversion

To type-constrain a variable, place a type literal to the left of the variable name in an assignment. For example:

[int]$foo = 42

You can use type casting to explicitly convert a value to a specific type. For example:

PS> $var = [int]'43'
PS> $var.GetType().Name
Int32

Type constraining ensures that only values of the specified type can be assigned to the variable. PowerShell performs an implicit conversion if you try to assign a value of a different type that can be converted to the constrained type. For more information, see the Implicit type conversion section of this article.

Numeric type conversion

Numeric types can be converted to any other numeric type as long as the target type is capable of holding the converted value. For example:

PS> (42.1).GetType().Name
Double
PS> $byte = [byte] 42.1
PS> $byte
42
PS> $byte.GetType().Name
Byte

The value 42.1 is a Double. When you cast it to a Byte, PowerShell truncates it to an integer 42, which is small enough to fit into a Byte.

When converting real numbers to integer types, PowerShell uses rounding rather than truncation, specifically using the rounding-to-nearest-even method. The following examples illustrate this behavior. Both values are rounded to the nearest even integer, 22.

PS> [byte]21.5
22
PS> [byte]22.5
22

For more information, see the Midpoint values and rounding conventions section of the Math.Round method.

Boolean type conversion

A value of any type can be coerced to a Boolean.

  • For numeric types, 0 converts to $false and any other value converts to $true.

    PS> [boolean]0
    False
    PS> [boolean]0.0
    False
    PS> [boolean]-1
    True
    PS> [boolean]1
    True
    PS> [boolean]42.1
    True
    
  • For other types, null values, empty strings, and empty arrays are converted to $false.

    PS> [boolean]''
    False
    PS> [boolean]@()
    False
    PS> [boolean]'Hello'
    True
    

    Other values, including empty hashtables convert to $true. Single-element collections evaluate to the Boolean value of their one and only element. Collections with more than 1 element are always $true.

    PS> [boolean]@(0)
    False
    PS> [boolean]@(0,0)
    True
    PS> [boolean]@{}
    True
    

String type conversion

A value of any type can be coerced to a String. The default conversion is to call the ToString() method on the object.

Arrays are converted to strings. Each element in the array is converted to a string, individually and joined to the resulting string. By default, the converted values are separated by a space. The separator can be changed by setting the $OFS preference variable.

PS> [string] @(1, 2, 3)
1 2 3

For more information about $OFS, see about_Preference_Variables.

A single string value can be converted to an instance of a type if the type implements a static Parse() method. For example, [bigint]'42' is the same as [bigint]::Parse('42', [cultureinfo]::InvariantCulture). The optional [cultureinfo]::InvariantCulture value binds to an IFormatProvider type parameter of the method. It ensures culture-invariant behavior of the conversion. Not all implementations of Parse() methods have this parameter.

Note

Conversion to and from strings is usually performed using the invariant culture. Invariant culture is based on, but not identical to, the US-English culture. Notably, it uses the period (.) as the decimal mark and US-style month-first dates by default. However, binary cmdlets perform culture-sensitive conversion during parameter binding.

Enum type conversion

PowerShell can convert Enum types to and from String instances. For example, the typecast string [System.PlatformId]'Unix' is the same as the enum value [System.PlatformId]::Unix. PowerShell also handles flag-based enums correctly for comma-separated values inside a string or as an array of strings. Consider the following examples:

[System.Reflection.TypeAttributes]'Public, Abstract'
[System.Reflection.TypeAttributes]('Public', 'Abstract')

These examples are equivalent to the enum expression:

[System.Reflection.TypeAttributes]::Public -bor
    [System.Reflection.TypeAttributes]::Abstract

Other type conversions

A single value (non-array) can be converted to an instance of a type if:

  • That type has a (public) single-parameter constructor
  • And the value is the same type or can be coerced to the type of the parameter

For example, the following two lines are equivalent:

[regex]'a|b'
[regex]::new('a|b')`

Implicit types

PowerShell also assigns types to literal values automatically.

Numeric literals are implicitly typed by default. Numbers are typed based on their size. For example, 42 is small enough to be stored as an Int32 type and 1.2 is stored as a Double. Integers larger than [int32]::MaxValue are stored as Int64. While 42 can be stored as a Byte and 1.2 can be stored as a Single type, the implicit typing uses Int32 and Double respectively. For more information, see about_Numeric_Literals.

Literal strings are implicitly typed as String. Single-character String instances can be converted to and from the Char type. However, PowerShell has no literal Char type.

Implicit type conversion

In certain contexts, PowerShell can implicitly convert values to other types. These contexts include:

  • Parameter binding
  • Type-constrained variables
  • Expressions using operators
  • Boolean contexts - PowerShell converts the conditional expressions of if, while, do, or switch statements to Boolean values, as previously described. For more information, see about_Booleans.
  • Extended Type System (ETS) type definitions - Type conversions can be defined in several ways:

Parameter binding conversions

PowerShell attempts to convert values passed to parameters to match the parameter type. Type conversion of parameter values occurs in cmdlets, functions, scripts, scriptblocks, or .NET methods where the parameter is declared with a specific type. Declaring a parameter with the type [Object] or not defining a specific type allows any value type to be passed to a parameter. Parameters can also have custom conversions defined by decorating parameters with ArgumentTransformationAttribute attribute.

For more information, see about_Parameter_Binding.

Best practices for parameter binding

For .NET methods, it's better to pass the exact type expected using a type casts where needed. Without exact types, PowerShell can select the wrong method overload. Also, new method overloads added in future versions of .NET can break existing code. For an extreme example of this problem, see this Stack Overflow question.

If you pass an array to a [string] typed parameter, PowerShell might convert the array to a string as previously described. Consider the following basic function:

function Test-String {
    param([string] $String)
    $String
}

Test-String -String 1, 2

This function outputs 1 2 because the array is converted to a string. To avoid this behavior, create an advanced function by adding the [CmdletBinding()] attribute.

function Test-String {
    [CmdletBinding()]
    param([string] $String)
    $String
}

Test-String -String 1, 2

For advanced functions, PowerShell refuses to bind the array to a non-array type. When you pass an array, PowerShell returns the following error message:

Test-String:
Line |
   7 |  Test-String -String 1, 2
     |                      ~~~~
     | Cannot process argument transformation on parameter 'String'. Cannot
     | convert value to type System.String.

Unfortunately, you can't avoid this behavior for .NET method calls.

PS> (Get-Date).ToString(@(1, 2))
1 2

PowerShell converts the array to the string "1 2", which is passed to the Format parameter of the ToString() method.

The following example shows another instance of the array conversion problem.

PS> $bytes = [byte[]] @(1..16)
PS> $guid = New-Object System.Guid($bytes)
New-Object: Cannot find an overload for "Guid" and the argument count: "16".

PowerShell treats the $bytes array as a list of individual parameters even though $bytes is an array of bytes and System.Guid has a Guid(byte[]) constructor.

This common code pattern is an instance of pseudo method syntax, which doesn't always work as intended. This syntax translates to:

PS> [byte[]] $bytes = 1..16
PS> New-Object -TypeName System.Guid -ArgumentList $bytes
New-Object: Cannot find an overload for "Guid" and the argument count: "16".

Given that the type of ArgumentList is [Object[]], a single argument that happens to be an array (of any type) binds to it element by element. The workaround is to wrap $bytes in an outer array so that PowerShell looks for a constructor with parameters that match the contents of the outer array.

PS> [byte[]] $bytes = 1..16
PS> $guid = New-Object -TypeName System.Guid -ArgumentList (, $bytes)
PS> $guid

Guid
----
04030201-0605-0807-090a-0b0c0d0e0f10

The first item of the wrapped array is our original [byte[]] instance. That value matches the Guid(byte[]) constructor.

An alternative to the array wrapping workaround is to use the intrinsic static new() method.

PS> [byte[]] $bytes = 1..16
PS> [System.Guid]::new($bytes)  # OK

Guid
----
04030201-0605-0807-090a-0b0c0d0e0f10

Type-constrained variable conversions

When you assign a value to a type-constrained variable, PowerShell attempts to convert the value to the variable type. If the value provided can be converted to the variable type, the assignment succeeds.

For example:

PS> [int]$foo = '43'
PS> $foo.GetType().Name
Int32

The conversion works because the string '43' can be converted to a number.

Operator conversions

PowerShell can implicitly convert the operands in an expression to produce a reasonable result. Also, some operators have type-specific behaviors.

Numeric operations

In numeric operations, even if both operands are the same numeric type, the result can be a different type, due to automatic type conversion to accommodate the result.

PS> [int]$a = 1
PS> [int]$b = 2
PS> $result = $a / $b
PS> $result
0.5
PS> $result.GetType().Name
Double

Even though both operands are integers, the result is converted to a Double to support the fractional result. To get true integer division, use the [int]::Truncate() or [Math]::DivRem() static methods. For more information, see Truncate() and DivRem().

In integer arithmetic, when the result overflows the size of the operands, PowerShell defaults to using Double for the results, even when the result could fit into an Int64 type.

PS> $result = [int]::MaxValue + 1
PS> $result
2147483648
PS> $result.GetType().Name
Double

If you want the result to be an Int64, you can cast the result type or operands.

PS> ([int64]([int]::MaxValue + 1)).GetType().Name
Int64

However, use care when casting results to a specific type. For example, type casting the result to the [decimal] type can lead to loss of precision. Adding 1 to the maximum Int64 value results in a Double type. When you cast a Double to a Decimal type, the result is 9223372036854780000, which isn't accurate.

PS> ([int64]::MaxValue + 1).GetType().Name
Double
PS> [decimal]([int64]::MaxValue + 1)
9223372036854780000

The conversion is limited to 15 digits of precision. For more information, see the Remarks section of the Decimal(Double) constructor documentation.

To avoid a loss of precision, use the D suffix on the 1 literal. By adding the D suffix, PowerShell converts the [int64]::MaxValue to a Decimal before adding 1D.

PS> ([int64]::MaxValue + 1D).GetType().Name
Decimal
PS> ([int64]::MaxValue + 1D)
9223372036854775808

For more information about numeric suffixes, see about_Numeric_Literals.

Usually, the left-hand side (LHS) operand of PowerShell operators determines the data type used in the operation. PowerShell converts (coerces) the right-hand side (RHS) operand to the required type.

PS> 10 - ' 9 '
1

In this example, the RHS operand is the string ' 9 ', which is implicitly converted to an integer before the subtraction operation. The same is true for the comparison operators.

PS> 10 -eq ' 10'
True
PS> 10 -eq '0xa'
True

There are exceptions when using arithmetic_ operators (+, -, *, /) with non-numeric operands.

When you use the - and / operands with strings, PowerShell converts both operands from strings to numbers.

PS> '10' - '2'
8
PS> '10' / '2'
5

By contrast, the + and * operators have string-specific semantics (concatenation and replication).

PS> '10' + '2'
102
PS> '10' * '2'
1010

When you use Boolean values with arithmetic operators, PowerShell converts the values to integers: $true becomes [int]1 and $false becomes [int]0.

PS> $false - $true
-1

The one exception is the multiplication (*) of two booleans.

PS> $false * $true
InvalidOperation: The operation '[System.Boolean] * [System.Boolean]' is not
defined.

For other LHS types, arithmetic operators only succeed if a given type custom-defines these operators via operator overloading.

Comparison operations

The comparison operators, such as -eq, -lt, and -gt, can compare operands of different types. The behavior of non-strings and non-primitive types depends on whether the LHS type implements interfaces such as IEquatable and IComparable.

The collection-based comparison operators (-in and -contains) perform per element -eq comparisons until a match is found. It's each individual element of the collection operand that drives any type coercion.

PS> $true -in 'true', 'false'
True
PS> 'true', 'false' -contains $true
True

Both examples return true because 'true' -eq $true yields $true.

For more information, see about_Comparison_Operators.