Created
December 14, 2025 14:28
-
-
Save SkyyySi/72995bdd0bce4ab5da32a31f3f4e56e9 to your computer and use it in GitHub Desktop.
Example for defining custom type conversion in PowerShell using [System.Management.Automation.PSTypeConverter]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env pwsh | |
| using namespace System | |
| using namespace System.Collections | |
| using namespace System.Collections.Generic | |
| using namespace System.Collections.ObjectModel | |
| using namespace System.Collections.Specialized | |
| using namespace System.ComponentModel | |
| using namespace System.Management.Automation | |
| using namespace System.Reflection | |
| class DictionaryPSTypeConverter : PSTypeConverter { | |
| # A parameterless constructor is required. | |
| DictionaryPSTypeConverter() {} | |
| # Gets invoked when [DictionaryPSTypeConverter] was added to the destination | |
| # type's type data. Not used here because you need to add it to the *exact* | |
| # output type (e.g. [Dictionary[string, int]]) - adding it to a generic type | |
| # definition (e.g. [Dictionary`2]) or an interface type (e.g. | |
| # [IDictionary]) does **not** work! | |
| [bool]CanConvertFrom([object]$SourceValue, [type]$DestinationType) { | |
| return $false | |
| } | |
| # Gets invoked when [DictionaryPSTypeConverter] was added to the source | |
| # values's type data, in this case [hashtable] and [ordered]. | |
| [bool]CanConvertTo([object]$SourceValue, [type]$DestinationType) { | |
| return ( | |
| (-not [object]::ReferenceEquals($null, $SourceValue)) -and | |
| (-not [object]::ReferenceEquals($null, $DestinationType)) -and | |
| ($SourceValue -is [IDictionary]) -and | |
| ($DestinationType -is [TypeInfo]) -and | |
| ($DestinationType.ImplementedInterfaces -contains [IDictionary]) | |
| ) | |
| } | |
| # This won't ever be called as [DictionaryPSTypeConverter]::CanConvertFrom | |
| # always returns false. | |
| [object]ConvertFrom([object]$SourceValue, [type]$DestinationType, [IFormatProvider]$FormatProvider, [bool]$IgnoreCase) { | |
| throw [NotSupportedException]::new( | |
| "[$($this.GetType())] does not support converting into arbitrary destination types." | |
| ) | |
| } | |
| [object]ConvertTo([object]$SourceValue, [type]$DestinationType, [IFormatProvider]$FormatProvider, [bool]$IgnoreCase) { | |
| [IDictionary]$Output = try { | |
| $DestinationType::new($SourceValue.Count, [StringComparer]::"Ordinal$(if ($IgnoreCase) { | |
| 'IgnoreCase' | |
| })") | |
| } catch { | |
| try { | |
| $DestinationType::new($SourceValue.Count) | |
| } catch { | |
| $DestinationType::new() | |
| } | |
| } | |
| $SourceValue.GetEnumerator() | ForEach-Object { | |
| [void]$Output.Add($_.Key, $_.Value) | |
| } | |
| return $Output | |
| } | |
| } | |
| # Register [DictionaryPSTypeConverter] to be invoked whenever PowerShell tries | |
| # to convert either a [hashtable] or an [ordered] into any other type, assuming | |
| # that no other conversion mechanism with a higher priority could be found (like | |
| # casting to [object], which is a base type of both, or to [psobject], which is | |
| # hard-coded to call [psobject]::AsPSObject()). | |
| [hashtable], [ordered] | Update-TypeData -TypeConverter ([DictionaryPSTypeConverter]) | |
| ################################################################################ | |
| # EXAMPLE # | |
| ################################################################################ | |
| # This would normally fail, because PowerShell would try to set each entry in | |
| # the source hashtable as a property instead of as an entry on the dictionary. | |
| # Beware that this will still happen if you use keys like `Count` that exist | |
| # on the given destination type, which can lead to some very odd behaviour. | |
| [Dictionary[string, int]]@{ | |
| Foo = 69 | |
| Bar = 420 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment