Search code examples
powershelltypescasting

PowerShell - cast a variable type based on a variable definition


I've got a hashtable with variables. The source of this hashtable is an empty parameters.json (for deployment of Azure resources) converted to a hashtable.

For this question I have simplified this hashtable to the following:

$deploymentParameters = [PSCustomObject]@{
    key1 = $null
    key2 = $null
    key3 = $null
}

During the execution of my script some of these variables will be filled from within my script (like generated objectnames), but others (like a region) will depend on user input.

I have defined all user input in a seperate definition. In this definition I include the name, userprompt and the type of the variable. The simplified definition looks like this:

$userInput = @(
    [PSCustomObject]@{
        Name       = "key1"
        UserPrompt = "Please enter the value for key1"
        Type       = [String]
    },
    [PSCustomObject]@{
        Name       = "key2"
        UserPrompt = "Please enter the value for key2"
        Type       = [String]
    },
    [PSCustomObject]@{
        Name       = "key3"
        UserPrompt = "Please enter the value for key3"
        Type       = [Int]
    }
)

In my script I can combine these with the following codeblock:

foreach ($parameter in $userInput)
{
    $value = Read-Host "Please enter the value for '$($parameter.UserPrompt)'"
    $deploymentParameters.$($parameter.Name) = $value
}

This works, yet $deploymentParameters.key3.GetType() will show this value is a string instead of an integer.

I tried casting the value to it's correct type with the following code:

foreach ($parameter in $userInput)
{
    $value = Read-Host "Please enter the value for '$($parameter.UserPrompt)'"
    [$parameter.Type] $deploymentParameters.$($parameter.Name) = $value
}

This will throw an error Missing type name after '['

I have been able to get this to work using a switch-case:

foreach ($parameter in $userInput)
{
    $value = Read-Host "Please enter the value for '$($parameter.UserPrompt)'"

    switch ($parameter.Type.Name)
    {
        "Int32"
        {
            [Int] $deploymentParameters.$($parameter.Name) = $value
        }
        "String"
        {
            [String] $deploymentParameters.$($parameter.Name) = $value
        }
        default
        {
            $null
        }
    }    
}

This solution isn't really maintainable though since I would have to include all cases I could encounter (or default anything not caught to a string).

Is there any way to actually cast to a specific type based on a parameter value?


Solution

  • Easiest way to cast them is using the -as operator, however you should note that when coercion fails, -as returns a null value.

    foreach ($parameter in $userInput) {
        $value = Read-Host "Please enter the value for '$($parameter.UserPrompt)'"
        $deploymentParameters.$($parameter.Name) = $value -as $parameter.Type
    }
    

    If instead you want to get an error when coercion failed, you can use LanguagePrimitives.ConvertTo:

    foreach ($parameter in $userInput) {
        $value = Read-Host "Please enter the value for '$($parameter.UserPrompt)'"
        $deploymentParameters.$($parameter.Name) = [System.Management.Automation.LanguagePrimitives]::ConvertTo(
            $value, $parameter.Type)
    }