Let's say I try to assign a string to a strongly typed integer variable:
[int]$bar = '1'
This works, as PowerShell is able to cast the string '1'
to an integer.
Now things are different if I attempt the same with a strongly typed boolean variable:
[boolean]$foo = 'foo'
Cannot convert value "System.String" to type "System.Boolean". Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1
or 0.
I find this confusing, as PowerShell at the same time allows an explicit cast from string to boolean:
[boolean]'foo'
True
Does anyone know the reason for this seemingly inconsistent behavior?
In most cases I would argue the following approach, rather than typed variables: Convert your values to the target type prior to assignment, and then let the type system infer the variable type:
$foo = [bool]"foo"
$foo = "foo" -as [bool]
$foo = $true -eq "foo"
(This is not an authoritative answer, but a best-guess)
Briefly mentioned in the about_Variables
help file:
TYPES OF VARIABLES
You can store any type of object in a variable, [...]
Windows PowerShell variables are "loosely typed," which means that they are not limited to a particular type of object. [...]
[... section about value-inferred types bla bla bla ...]
You can use a type attribute and cast notation to ensure that a variable can contain only objects of the specified type or objects that can be converted to that type. If you try to assign a value of another type, Windows PowerShell tries to convert the value to its type. If it cannot, the assignment statement fails.
To use cast notation, enter a type name, enclosed in brackets, before the variable name (on the left side of the assignment statement).
Although "type attribute" and the distinct constraint that this applies only during assignment is used nowhere else in the documentation, but (to me at least) indicates that this is a special case of explicit casting.
When you add an explicit cast notation to the variable, during assignment, as described above, PowerShell adds an ArgumentTypeConverterAttribute
to the variable, and PowerShell's type conversion magic is suddenly overridden by a type-specific Transform()
method:
PS C:\> $var = 5
PS C:\> Get-Variable var |fl *
Name : var
Description :
Value : 5
Visibility : Public
Module :
ModuleName :
Options : None
Attributes : {}
PS C:\> [int]$var = 5
PS C:\> Get-Variable var |fl *
Name : var
Description :
Value : 5
Visibility : Public
Module :
ModuleName :
Options : None
Attributes : {System.Management.Automation.ArgumentTypeConverterAttribute}
If we repeat this experiment with a boolean type, you can see how the ArgumentTypeConverterAttribute
transform is much more restrictive than the normal conversion "magic":
PS C:\> [bool]$foo = $true
PS C:\> (Get-Variable foo).Attributes[0].Transform($ExecutionContext,"foo")
Exception calling "Transform" with "2" argument(s): "Cannot convert value "System.String" to type "System.Boolean". Boolean parameters
accept only Boolean values and numbers, such as $True, $False, 1 or 0."
At line:1 char:1
+ (Get-Variable foo).Attributes[0].Transform($ExecutionContext,"foo")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentTransformationMetadataException
Again in this type of conversion Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or 0., whereas powershell itself generally interprets any non-empty, non-zero or non-null value to be $true
when implicitly converted to [bool]
.
in other words:
[bool]$SomeVariable = [bool]"bar"
^ ^ ^
| | |
| during assignment |
This is a type attribute|
This is a cast notation
Even though they look just the same
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With