Search code examples
powershellpowershell-7.3

Why I cannot change the arg's type within a PowerShell function?


I'm a novice in PowerShell. Recently, I have found if I try to change the arg's type within a PowerShell function, i.e., assigning a different type of value to a variable that has the same name as a function's arg, it may incur errors, which is quite different from the reference concepts in Python.

An easy test is as follows, which will bring an error Cannot convert value "????" to type "System.Int32". Error: "The input string '????' was not in a correct format.":

function Update-Value {
    param (
        [int]$inputValue
    )
    $inputValue = "????" 
    Write-Host "Inside function: inputValue = $inputValue"
}
Update-Value -inputValue 10

If do not claim the type, there are no errors:

function Update-Value {
    param (
        $inputValue
    )
    $inputValue = "????" 
    Write-Host "Inside function: inputValue = $inputValue"
    $inputValue = 10
    Write-Host "Inside function: inputValue = $inputValue"
}
Update-Value -inputValue 10
# Inside function: inputValue = ????
# Inside function: inputValue = 10

For a long time, I used the latter most and I didn't pay attention to the parameter types. I would have thought that in PowerShell, a variable name is just a reference. But now I think it's not. Even though I have searched and learned from PowerShell's Doc, maybe I missed it, I do not know the actually function and function args mechanism well.

It seems that:

  • If state the type of a function arg, any assignment in the function will automatically and implicitly be followed by a type converting procedure.
  • If the converting does not work, it throws errors.
  • If do not claim the type of a function arg, any assignment works well, like the variable names are all references.

My problem is, what is the PowerShell function mechanism for arg type's converting and restricting? And what’s the usual practice when I want to assign a different type of value to a function arg? It that create a new variable with a similar name, such as one with _ afront?


Solution

  • If state the type of a function arg, any assignment in the function will automatically and implicitly be followed by a type converting procedure.

    Correct, if the instance type passed as argument doesn't match with the parameter type then PowerShell attempts a conversion. See Conversions for in depth details on this.

    If the converting does not work, it throws errors.

    This is also correct. Last step described in 6.17 Conversion during parameter binding.

    If do not claim the type of a function arg, any assignment works well, like the variable names are all references.

    If you don't use a type for your parameter, PowerShell defaults to object and all .NET types inherit from it, thus, no conversion is required. This is also noted in Conversions during parameter binding:

    • If the parameter type is object or is the same as the type of the argument, the argument's value is passed without conversion.

    My problem is, what is the PowerShell function mechanism for arg type's converting and restricting? And what’s the usual practice when I want to assign a different type of value to a function arg? It that create a new variable with a similar name, such as one with _ upfront?

    When you define a specific type for your parameter, you're essentially constraining it, meaning that a conversion will be attempted to any value assigned to it when the types don't match.

    So, answering your question as to if you must create a new variable with a similar name, the answer is no, there is no need to. You can constrain the variable again to a different target type, or, if you want to assign anything to it, you can constrain it back to object or psobject:

    function Update-Value {
        param (
            [int] $inputValue # Constrained to `int`
        )
    
        [psobject] $inputValue = '????' # Constrained back to `psobject`
        Write-Host "Inside function: inputValue = $inputValue"
        $inputValue = 10
        Write-Host "Inside function: inputValue = $inputValue"
    }
    
    Update-Value -inputValue 10