Hmmm...So Here is what I think. I think it is an effect of the way Powershell now unrolls properties on array objects. Powershell definitely is truthy.
The first test if ($cObj.Bool)
does work the way one would expect. The second test if ($coArray.Bool)
causes powershell to create a new array that has just the contents of that property from each object. This can be shown by:
C:\Users\Rob> $coArray.bool
False
False
So in truthy Powershell world this is a thing that does exist so it is true.
The third test if ($coArray.Bogus)
pretty much does the same thing. This can be shown by:
Get-Member -InputObject $coArray.bogus
TypeName: System.Object[]
Name MemberType Definition
---- ---------- ----------
Count AliasProperty Count = Length
Add Method int IList.Add(System.Object value)
Address Method System.Object&, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a...
Clear Method void IList.Clear()
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
CompareTo Method int IStructuralComparable.CompareTo(System.Object other, System.Collections.ICo...
Contains Method bool IList.Contains(System.Object value)
CopyTo Method void CopyTo(array array, int index), void CopyTo(array array, long index), void...
Equals Method bool Equals(System.Object obj), bool IStructuralEquatable.Equals(System.Object ...
Get Method System.Object Get(int )
GetEnumerator Method System.Collections.IEnumerator GetEnumerator(), System.Collections.IEnumerator ...
GetHashCode Method int GetHashCode(), int IStructuralEquatable.GetHashCode(System.Collections.IEqu...
GetLength Method int GetLength(int dimension)
GetLongLength Method long GetLongLength(int dimension)
GetLowerBound Method int GetLowerBound(int dimension)
GetType Method type GetType()
GetUpperBound Method int GetUpperBound(int dimension)
GetValue Method System.Object GetValue(Params int[] indices), System.Object GetValue(int index)...
IndexOf Method int IList.IndexOf(System.Object value)
Initialize Method void Initialize()
Insert Method void IList.Insert(int index, System.Object value)
Remove Method void IList.Remove(System.Object value)
RemoveAt Method void IList.RemoveAt(int index)
Set Method void Set(int , System.Object )
SetValue Method void SetValue(System.Object value, int index), void SetValue(System.Object valu...
ToString Method string ToString()
Item ParameterizedProperty System.Object IList.Item(int index) {get;set;}
IsFixedSize Property bool IsFixedSize {get;}
IsReadOnly Property bool IsReadOnly {get;}
IsSynchronized Property bool IsSynchronized {get;}
Length Property int Length {get;}
LongLength Property long LongLength {get;}
Rank Property int Rank {get;}
SyncRoot Property System.Object SyncRoot {get;}
So as pointed out, just doing one object in the array causes all the tests to be false. This is because with just one item in the array means there is only one property for it to unroll. Powershell unrolls this to a singleton object not an array of values. So if there is no property with that name the singleton is null. See here:
C:\Users\Rob> $b = @((New-Object PSCustomObject -Property @{'notBogus'='foo'}))
C:\Users\Rob> $b
notBogus
--------
foo
C:\Users\Rob> gm -in $b.Notbogus
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
CompareTo Method int CompareTo(System.Object value), int CompareTo(string strB), int IComparab...
Contains Method bool Contains(string value)
CopyTo Method void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int co...
EndsWith Method bool EndsWith(string value), bool EndsWith(string value, System.StringCompari...
.
.
.
C:\Users\Rob> $b.bogus -eq $null
True
So as you can see, $b.NotBogus is simply a single value so it would test appropriately.
Still not sure about the counter example that @tessellatingHeckler presented. I'll keep looking.