Why does PowerShell unwrap PSObject wrappers differently for array[] and object[]?

August 6, 2026 3 answers
Asked by MellowPine47
I'm trying to understand two related PowerShell behaviors. These functions receive the output of Get-ChildItem: function Test-Weird1 { param([array[]]$InputObject) $obj = $InputObject[0][0] $obj.GetType().FullName ($obj -is [PSObject]) } function Test-Weird2 { param([object[]]$InputObject) $obj = $InputObject[0] $obj.GetType().FullName ($obj -is [PSObject]) } Test-Weird1 (Get-ChildItem) returns System.IO.DirectoryInfo and False, while Test-Weird2 (Get-ChildItem) returns System.IO.DirectoryInfo and True. There is also this behavior: $obj = (Get-ChildItem)[0].PSObject $obj.GetType() -eq [System.Management.Automation.PSObject] # True $obj -is [System.Management.Automation.PSObject] # False Why does the first parameter type change the indexing and PSObject result, and why does the PSObject property report that it has the PSObject type while the -is operator says otherwise?

Answers

QuasarMoth8
The first difference comes from the parameter conversion. `[array[]]` means an array whose elements are themselves arrays, so the input is coerced into a jagged array. `$InputObject[0][0]` reaches the underlying .NET `DirectoryInfo` object. During that conversion, PowerShell removes the `PSObject` wrapper, which is why `-is [PSObject]` is false. `[object[]]` is just a one-dimensional object array. In that case, `$InputObject[0]` is the original PowerShell object, including its wrapper, so `-is [PSObject]` is true. The wrapperโ€™s base object is still a `System.IO.DirectoryInfo`, which explains the type name returned by `GetType()`. PowerShell normally preserves extended type system members when it unwraps and later rewraps objects, although some typesโ€”strings are a common exampleโ€”can lose added members during conversion.
Reply to this answer
CobaltRaven62
The `.PSObject` member is not simply the same thing as asking whether the value itself is a `PSObject`. It exposes an object view represented by a member-set object, essentially a PowerShell inspection interface around the original value. So in this example, `$obj.GetType()` reports the runtime type of that view, `System.Management.Automation.PSObject`, but the `-is` check tests the value in the other direction and does not consider the member-set view to be an instance of `PSObject`. In practice, `.PSObject`, `.PSAdapted`, and related intrinsic members are special PowerShell member sets rather than ordinary properties containing a normal PSObject wrapper.
Reply to this answer
RiverKite903
One extra edge case: indexing an empty result can leave you with `$null`, so `$InputObject[0].GetType()` may fail. If you need a consistently safe first-element lookup, use `@($InputObject)[0]`. In PowerShell 7, a null-conditional call such as `($obj)?.GetType().FullName` can also avoid calling a method on `$null`.
Reply to this answer

Add your answer

Answers are reviewed before publication.

Related Discussions