Search code examples
powershellnulltri-state-logic

Is it possible to have [Nullable[bool]] in a bool[] in PowerShell


Is it possible to have [Nullable[bool]] in a bool[] in PowerShell? I tried different solution, approaches but fail to get proper $null, $true, $false for a parameter? Also it seems that [cmdletbinding] changes how things works as well.

enum BoolNull {
    null = $null
    true = $true
    false = $false
}
function Test-Array0 {
    param (
        [bool[]] $thisValue
    )
    if ($thisValue -eq $null) {
        Write-Output 'Null found'
    }
}
function Test-Array1 {
    param (
        [bool[]] $thisValue
    )
    foreach ($value in $thisValue) {
        if ($value -eq $null) {
            Write-Output 'Null found'
        }
    }
}
function Test-Array2 {
    [CmdletBinding()]
    param (
        [bool[]] $thisValue
    )
    if ($thisValue -eq $null) {
        Write-Output 'Null found'
    }
}


function Test-Array {
    [CmdletBinding()]
    param (
        [AllowEmptyCollection()] [AllowNull()][ValidateSet($null, $true, $false)] $thisValue
    )
    if ($thisValue -eq $null) {
        Write-Output 'Null found'
    }
}

# this works
function Test-Test {
    [CmdletBinding()]
    param (
        [nullable[bool]] $thisValue
    )
    if ($thisValue -eq $null) {
        Write-Output 'Null found'
    }
}

function Test-Array5 {
    param (
        [boolnull[]] $thisValue
    )
    foreach ($value in $thisValue) {
        if ($value -eq 'null') {
            Write-Output 'Null found'
        }
    }
}

Test-Array0 -thisValue $null #this works
Test-Array -thisValue $null # this doesn't work
Test-Array -thisValue $null, $null, $true # this doesn't work
Test-Array1 -thisValue $null
Test-Array2 -thisValue $null # this
Test-Test -thisValue $null # this works 
Test-Array5 -thisValue null, true, null # this works but is completely useless

enter image description here


Solution

  • This is a limitation of the bool type. When you strictly-type the parameter, it can only take $true, $false, 0, and 1. In order to achieve what you want, you can use a [ValidateSet] attribute:

    [CmdletBinding()]
    param(
        [Parameter(Position = 0, Mandatory)]
        [ValidateSet($null, $true, $false)]
        [object] $ThisValue
    )
    

    As a side-note, there used to be a bug with powershell (might still be present) where comparing $null on the right side will cause nothing to be returned, causing logic to fall out of the statement, so it's best to compare on the left:

    if ($null -eq $ThisValue) {
    

    After testing your example, I was unable to replicate your problem, however:

    function Test-Nullable {
        [CmdletBinding()]
        param(
            [nullable[bool]] $Value
        )
    
        if ($null -eq $Value) {
            'Yes'
        } else {
            $Value
        }
    }
    

    and in array format:

    function Test-Nullable {
        [CmdletBinding()]
        param(
            [nullable[bool][]] $Value
        )
    
        foreach ($bool in $Value) {
            if ($null -eq $bool) {
                'Yes'
            } else {
                $bool
            }
        }
    }
    
    Test-Nullable 5, 3, $null, $true, $false, 0
    True
    True
    Yes
    True
    False
    False