To complement HAL9256's helpful answer:
First, some background information; find a working solution in the bottom section.
IntelliSense for variables in Visual Studio Code works if their type is either:
- explicitly declared (e.g.,
[datetime] $var = ...
)
- or can be inferred from an assigned value.
If an assignment is based on a command (cmdlet, function, script) call, the type can only be inferred from commands with explicitly defined output type(s):
- many, but by no means all, cmdlets do declare their output types
- functions and scripts must use the
[OutputType(<type>)]
attribute.
Additionally, with the nondescript [pscustomobject]
type returned by ConvertFrom-Json
- which has no inherent properties, only those you add on demand; a "property bag" - you only get IntelliSense:
- if a variable was assigned from a custom-object literal (e.g.,
$var = [pscustomobject] @{ one = 1; two = 2 }
)
- if you cast a custom object to a specific type, assuming instances of that type can be constructed from the custom object's properties, something that PowerShell makes easy - see this answer.
Solution with a custom class (PSv5+)
Visual Studio Code's IntelliSense (via the PowerShell Extension) does recognize the members of instances of PS custom classes defined via the PSv5+ class
statement.
You can therefore use a custom class to mirror the structure of the JSON object you're loading and convert the [pscustomobject]
"property bags" returned by ConvertFrom-Json
to an instance of that class by way of a cast.
Note: The inherent limitation of this approach is that your class must anticipate all property names that the underlying JSON objects contain, and the two must be kept in sync; otherwise:
- if a property name changes on the JSON side, your code will break if the class definition isn't updated accordingly.
- if new properties are added on the JSON side, these will be inaccessible unless the class definition is updated accordingly.
class
definitions can either be:
- directly embedded in a script
- imported from modules via the
using module
statement (note that using Import-Module
does not load a module's classes).
To implement the solution at hand, you can use a class
definition in one of two ways:
(a) Define a class
directly in your script that matches the structure of the JSON objects, and cast the [pscustomobject]
instance returned from ConvertFrom-Json
to that type; a variable assigned this way supports IntelliSense.
(b) Wrap the JSON-loading functionality in a module that performs the above inside the module, and passes the class
instance out from a function that declares its [OutputObject()]
to be of that type; code that imports that module with using module
will then get IntelliSense for variables that capture the output from that function.
A simple demonstration of (a):
# Define a class whose properties mirror the underlying JSON.
class Config {
$foo
$bar
}
# Load the JSON and cast the resulting [pscustomobject] to the class.
# Note: This cast only works if the JSON object's set of properties
# is either the same as that of the [Config] type or a subset of it.
[Config] $config = '{ "foo": "bar", "bar": 42 }' | ConvertFrom-Json
# Variable $config supports IntelliSense, because its is now known
# as type Config.
$config. # shows list of properties