question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Write-Output result identifies as `[pscustomobject]` (`-is [pscustomobject]`)

See original GitHub issue

Prerequisites

Steps to reproduce

(Write-Output 1) -is [pscustomobject]  # unexpectedly True, although .GetType() returns Int32
(1) -is [pscustomobject]  # False, as expected

$x = Write-Output 1
$x -is [pscustomobject]  # also unexpectedly True
$y = 1
$y -is [pscustomobject]  # False, as expected

The issue seems reproducible in both PowerShell 5.1 and PowerShell Core 7.2.0-rc.1.

Expected behavior

False each time (`Int32` is not a `pscustomobject`).

Actual behavior

PowerShell seems to think any object returned by `Write-Output` is somehow also `pscustomobject`.

Result of this issue is we can have a pair of “almost exactly the same” objects, but one of them thinks it’s also a pscustomobject:

$x = 1
$y = echo $x  # I'd assume $y is the exact same thing as $x here...

$x -is [pscustomobject]
$y -is [pscustomobject]  # ...but it's not, because these two checks return different things.

Error details

No response

Environment data

Name                           Value
----                           -----
PSVersion                      7.2.0-rc.1
PSEdition                      Core
GitCommitId                    7.2.0-rc.1
OS                             Microsoft Windows 10.0.19043
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:9

github_iconTop GitHub Comments

1reaction
jhoneillcommented, Nov 5, 2021

Yep, you more or less got it. Anything passed through the pipeline is essentially wrapped in a PSObject wrapper, and checking -is [psobject] (and by extension -is [pscustomobject] as @jborean93 points out, as they point to the same type) is pretty meaningless.

I think there is a need for some documentation on to explain that type accelerators (a) exist and (b) don’t do what one might expect : for example

1gb -is [int]
true

2gb -is [int] 
false

This makes sense only when one knows that int serves as an alias for [int32]

[int]

IsPublic IsSerial Name                                     BaseType                                                                                                                                               
-------- -------- ----                                     --------                                                                                                                                               
True     True     Int32                                    System.ValueType 

[int].FullName
System.Int32

So knowing 1gb is an int32 but 2gb is an int64, and -is [int] tests for -is [System.Int32] makes sense of the the example.
System. is not needed needed on a type name - there is an implicit using namespace system so we can use -is [int32] and in lot of cases, see the table below, there’s an argument for using Int32 instead of int, Boolean instead of bool etc. (moving a function to a cmdlet in C# doesn’t need to change type names).
with pscustomobject we get

 [PSCustomObject]

IsPublic IsSerial Name                                     BaseType                                                                                                                                               
-------- -------- ----                                     --------                                                                                                                                               
True     True     PSObject                                 System.Object                                                                                                                                          

[PSCustomObject].FullName
System.Management.Automation.PSObject

So -is [PSCustomObject] does -is [System.Management.Automation.PSObject] - I think this is a unique case where is System.Management.Automation.X type and [X] is a type accelerator which points to a different type giving PSCustomObject its split personality: interchangeable with psobject in some operations, but not, for example, in a cast

([PSCustomObject]@{Foo='Bar'}).GetType().FullName
System.Management.Automation.PSCustomObject

 ([PSobject]@{Foo='Bar'}).GetType().FullName
System.Collections.Hashtable

IMHO the original design wasn’t ideal and the behaviour it gives falls in a awkward triple

  • It is not intuitive
  • Nor is it not easily discoverable -it is “arcane” knowledge discovered by asking the right people: there is no about_pscustomobject or similar topic and it is not covered in System.Management.Automation.PSCustomObject
  • It is probably too late to change.

And the best “fix” for that is to document it. For reference any such help probably needs to explain the following:

TypeAccelerator Undelying type
bool System.Boolean
float System.Single
int System.Int32
short System.Int16
long System.Int64
pscustomobject System.Management.Automation.PSObject
ref System.Management.Automation.PSReference
switch System.Management.Automation.SwitchParameter
bigint System.Numerics.BigInteger
ushort System.UInt16
uint System.UInt32
ulong System.UInt64
xml System.Xml.XmlDocument
semver System.Management.Automation.SemanticVersion
adsi System.DirectoryServices.DirectoryEntry
adsisearcher System.DirectoryServices.DirectorySearcher
wmiclass System.Management.ManagementClass
wmi System.Management.ManagementObject
wmisearcher System.Management.ManagementObjectSearcher
0reactions
msftbot[bot]commented, Nov 7, 2021

This issue has been marked as answered and has not had any activity for 1 day. It has been closed for housekeeping purposes.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Write-Output result identifies as `[pscustomobject]` (`
Write-Output result identifies as `[pscustomobject]` (`-is [pscustomobject]`) · Summary · Jobs. Rebase · Run details. Usage · Workflow file.
Read more >
Everything you wanted to know about PSCustomObject
PSCustomObject is a simple way to create structured data.
Read more >
powershell where-object changes type of psobject to ...
As you can see, the type get's changed. I need to keep the Object as it is (before). How to workaround that? Thanks...
Read more >
Chapter 21. Creating objects for output - PowerShell in Depth
The object type— PSObject —is one provided by PowerShell specifically for this purpose. ... If you pipe $obj to Get-Member, look at the...
Read more >
BAMCIS.Common.psm1 1.0.0.1
Converts a PSCustomObject to a Hashtable. ... The cmdlet takes a PSCustomObject and converts all of its property key/values to a Hashtable. You...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found