Skip to content

Instantly share code, notes, and snippets.

@SkyyySi
Created December 14, 2025 14:28
Show Gist options
  • Select an option

  • Save SkyyySi/72995bdd0bce4ab5da32a31f3f4e56e9 to your computer and use it in GitHub Desktop.

Select an option

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]
#!/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