Search code examples
arrayspowershellunion-types

Is union type available in PowerShell


Typed array like $arrIntOnly = [Int[]]@(1, 2, 3) is useful to ensure that all elements are valid type, but is it possible to define multiple types like $arrIntOrString = [[Int | String][]]@(1, "two", 3)?


Solution

  • PowerShell does not have a union data type, but you can use PowerShell's default arrays, which are [object[]]-typed and can therefore contain elements of any type:

    # Note: @(...) around the array isn't strictly necessary.
    $arrIntOrString = 1, 'two', 3
    

    There's no direct way to limit what specific types are permitted.

    However, you can use a validation attribute - of type System.Management.Automation.ValidateScriptAttribute in this case - to enforce that the elements are limited to specified types:

    [ValidateScript({ $_ -is [int] -or $_ -is [string] })] $arrIntOrString = 1, 'two', 3
    

    The above would enforce the specified types both on initial assignment and later modifications; e.g., the following attempt to later "add"[1] an element of a non-permitted type would fail:

    # FAILS, because $true (of type [bool]) is neither an [int] nor [string]
    $arrIntOrString += $true
    

    Unfortunately, the error message is somewhat obscure: MetadataError: The variable cannot be validated because the value System.Object[] is not a valid value for the arrIntOrString variable.

    Note that this validation is fairly slow, given that a script block ({ ... }) must be executed for each element.


    You can also apply this technique to parameter declarations, which in PowerShell (Core) 7+ allows you to define a custom error message:

    Function Foo {
      param(
        # Note: Use of ErrorMessage requires PS 7+
        [ValidateScript({ $_ -is [int] -or $_ -is [string] }, ErrorMessage = 'Please pass an integer or a string.')]
        $IntOrString
      )
      $IntOrString
    }
    
    Foo -IntOrString 1.0 # Fails validation 
    

    [1] Arrays are fixed-length data structures, so what PowerShell does when you "add" to an array with += is to create a new array behind the scenes, with the new element(s) appended. Efficiently extensible alternatives to arrays are the non-generic System.Collections.ArrayList type (e.g., [System.Collections.ArrayList] (1, 'two', 3)) and the generic System.Collections.Generic.List`1 type (e.g., [System.Collections.Generic.List[object]] (1, 'two', 3)). Then use the .Add() method to add to these collections; note that the ArrayList.Add() method returns a value, which you can suppress with $null = .... To initialize an empty collection of either type, cast @() to the type literal or call its static ::new() method.