3

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)?

mklement0
  • 382,024
  • 64
  • 607
  • 775
Takuya HARA
  • 499
  • 1
  • 3
  • 17

1 Answers1

6

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 2
    @theberzi, no, PowerShell doesn't have a union data type (I've updated the answer to make that clear), and this answer offers an _approximation_, in the context of _arrays_, which is what the question is about. However, you can apply the `ValidationScript` technique to parameter declarations as well - please see my update. – mklement0 Jun 15 '22 at 07:25