Transpilers/Parameters/ValidateTypes.psx.ps1

<#
.Synopsis
    Validates if an object is one or more types.
.Description
    Validates if an object is one or more types.
    
    This allows for a single parameter to handle multiple potential types.
.Example
    {
        param(
        [ValidateTypes(TypeName="ScriptBlock","string")]
        $In
        )
    } | .>PipeScript
.Example
    {"hello world"} |
        Invoke-PipeScript -ScriptBlock {
            param(
            [vfp()]
            [ValidateTypes(TypeName="ScriptBlock","string")]
            $In
            )

            $In
        }
.Example
    1 |
        Invoke-PipeScript -ScriptBlock {
            param(
            [vfp()]
            [ValidateTypes(TypeName="ScriptBlock","string")]
            $In
            )

            $In
        }
#>

[CmdletBinding(DefaultParameterSetName='Parameter')]
param(
# The name of one or more types.
# Types can either be a .NET types of .PSTypenames
# TypeNames will be treated first as real types, then as exact matches, then as wildcards, and then as regular expressions.
[Parameter(Mandatory,Position=0)]
[string[]]
$TypeName,

# The variable that will be validated.
[Parameter(ValueFromPipeline,ParameterSetName='VariableExpressionAST')]
[Management.Automation.Language.VariableExpressionAST]
$VariableAST
)


$checkTypes = @"
`$validTypeList = '$($typeName -join "','")'
"@
 + {
$thisType = @(
    if ($_.GetType) {
        $_.GetType()
    }
    $_.PSTypenames
)
$IsTypeOk = 
    $(@(foreach ($validTypeName in $validTypeList) {
        $realType = $validTypeName -as [type]
        foreach ($Type in $thisType) {
            if ($Type -is [type]) {
                if ($realType) {
                    $realType -eq $type -or
                    $type.IsSubClassOf($realType) -or
                    ($realType.IsInterface -and $type.GetInterface($realType))
                } else {
                    $type.Name -eq $realType -or 
                    $type.Fullname -eq $realType -or 
                    $type.Fullname -like $realType -or $(
                        ($realType -as [regex]) -and 
                        $type.Name -match $realType -or $type.Fullname -match $realType
                    )
                }
            } else {
                $type -eq $realType -or 
                $type -like $realType -or (
                    ($realType -as [regex]) -and 
                    $type -match $realType
                )
            }
        }
    }) -eq $true) -as [bool]
}

if ($PSCmdlet.ParameterSetName -eq 'Parameter') {
[scriptblock]::Create(@"
[ValidateScript({
$checkTypes
if (-not `$isTypeOk) {
    throw "Unexpected type '`$(@(`$thisType)[0])'. Must be '$($typeName -join "','")'."
}
return `$true
})]
$(
    if ($psCmdlet.ParameterSetName -eq 'Parameter') {
        'param()'
    } else {
        '$' + $VariableAST.variablePath.ToString()
    }
)
"@
)
}