It's possible in PowerShell to define an output type on scripts. Consider myScript.ps1
:
[OutputType([String])]
param(
[string]$name
)
The following returns String
:
(Get-Command .\myScript.ps1).OutputType.Name
But I would like to specify that a script returns text/json
or text/xml
. What would be a good way of doing that?
Inventing types for OutputType (e.g. [String.JSON]
) does not work.
There are two independent, but complementary mechanisms for declaring output types:
Important: Both ways of declaring output types are informative only and aren't enforced by PowerShell at runtime.
Mechanism A: Using the OutputType
attribute above the param()
declaration in a script or function, as in the question:
Always use this mechanism, and, if necessary, supplement with mechanism B.
Only recognizes full type names of .NET types or the name of PowerShell type accelerators and no up-front validation is performed, but if an unrecognized type is encountered at invocation time:
- if originally specified as a string (e.g.,
'System.Text.Encoding'
): it is quietly ignored.
- if originally specified as a type literal (e.g.,
[System.Text.Encoding]
): the function call breaks on invocation
This strict, type-based integration enables tab completion / IntelliSense (command line / Visual Studio Code)
As for when you may want to use mechanism B too:
If the .NET type names don't tell the full story, as in the question.
If a function outputs multiple types and you want to verbally describe what types are output when.
[Supplemental] Mechanism B: Using the .OUTPUTS
section of comment-based help:
Accepts free-form descriptions; while referencing actual type names makes sense, doing so is not enforced.
While you can use this mechanism alone, doing so forgoes the advantages of tab completion and IntelliSense.
Therefore, use it to supplement mechanism A, if needed, but note that:
If both mechanisms are used, Get-Help
only shows the mechanism B definitions.
Since the two mechanisms are independent, you must manually ensure that the free-form information specified is complete and consistent with the mechanism A declarations.
To examine the output-type information with human eyeballs, use (Get-Help <cmd>).returnvalues
(caveat: requires help to be defined, such as via comment-based help) or read the OUTPUTS
section of the output from Get-Help -Full <cmd>
.
This will either show the free-form .OUTPUTS
content or, in its absence, the full type names of the [OutputType[]]
-declared types.
For programmatic access, use (Get-Command <cmd>).OutputType
, which returns [System.Management.Automation.PSTypeName]
instances whose .Type
property contains the actual type.
Details below, starting with the answer to the original question.
Mechanism A: Using OutputType
attribute(s) above params()
:
You may only specify .NET types as arguments to the OutputType
attribute, so strings such as text/json
or text/xml
that reflect MIME types won't work.
If you want string output, you've already chosen the closest approximation of your MIME types in terms of .NET types: [OutputType([String])]
You may specify multiple types in a single [OutputType()]
attribute, or you may use individual attributes.
You must use individual attributes if you want to map output types to specific parameter sets (e.g., [OutputType([string], ParameterSetName='NameOnly')]
).
As of Windows PowerShell v5.1 / PowerShell Core v6.0.1, however, this information is neither used by tab completion / IntelliSense nor reflected in the output from Get-Help -Full
.
Note: For a type to be recognized by the OutputType
attribute at invocation time,
use either the full type name (e.g., [System.Text.RegularExpressions.Match]
rather than just [Match]
) or the name of a PowerShell type accelerator, such as [regex]
.
- When in doubt, type
[<fullTypeName>]
at the prompt and see if it is recognized as a type; additionally you may choose between specifying the type as a string (e.g., 'System.Text.Encoding'
) or as a type literal (e.g., (e.g., [System.Text.Encoding]
), which has behavioral implications - see below.
if the specified type isn't present at invocation time, e.g., because the assembly containing the type hasn't been loaded, the behavior depends on how the output was declared:
- if originally specified as a string: it is quietly ignored.
- if originally specified as a type literal: the function call breaks on invocation
Beyond that,
either: simply describe the specific types of strings that your cmdlet outputs in its help text, such as via mechanism B described below,
or: create custom .NET types whose names reflect the desired conceptual type, and specify them in the OutputType
attribute - see below.
As stated, despite its constrained nature, the OutputType
attribute is purely informative at runtime - it is, however, used for tab completion and IntelliSense (Visual Studio Code).
Example of using a custom type:
# Define an empty custom type for the sole purpose for being able to use
# it with the OutputType attribute.
# Note: On first call, this may take a second or two, as the code is being
# compiled.
Add-Type @'
namespace org.example {
public class text_json {}
}
'@
function foo {
# Reference the custom type defined above; full type name required.
[OutputType([org.example.text_json])]
param(
[string]$name
)
}
You then get:
> (Get-Command foo).OutputType.Name
org.example.text_json
Note that the [System.Management.Automation.PSTypeName]
instances that .OutputType
outputs are not the same as the [type]
instances you get when you inspect a type directly:
The .Name
property of [System.Management.Automation.PSTypeName]
corresponds to the .FullName
property of [type]
, so you get the full type name if the type is recognized (available in the session); otherwise, it is the name as originally specified.
Mechanism B: Using the .OUTPUTS
section in comment-based help:
Conceptual help topic Get-Help about_Comment_Based_Help
describes how section .OUTPUTS
inside comment-based help for scripts and functions can be used to list and describe output types.
Note: Similarly, the .INPUTS
section can be used to describe supported input types, though that is arguably less interesting, given that specifying input types is an integral part of parameter declaration and documentation. By and large, .INPUTS
functions analogously to .OUTPUTS
, with only differences mentioned below.
An .OUTPUTS
section uses the following format suggested by the examples in the help topic, but note that the text is ultimately free-form, and no structure is enforced.
<type-name> <optional-description>
Even though the help topic (as of PSv5) doesn't mention it, it seems that in the event of multiple output types, each should be described in its own .OUTPUTS
section.
That said, the free-form format allows you to describe multiple output-type descriptions in a single section.
Example, taking advantage of the free-form format to describe the output in terms of MIME types:
<#
.SYNOPSIS
Does stuff.
.OUTPUTS
text/json. In case of x, returns a JSON [string].
.OUTPUTS
text/xml. In case of y, returns an XML [string].
#>
function foo {
param()
}
Note that when using Get-Help
to view the help as a whole, the (aggregated) .OUTPUTS
(and .INPUTS
) sections are only shown with Get-Help -Full
.
Querying that information programmatically essentially yields the OUTPUTS
section from
Get-Help -Full
verbatim (with individual .OUTPUTS
sections in the source concatenated with an empty line in between, and extra trailing empty lines):
> (Get-Help foo).returnvalues
text/json. In case of x, returns a JSON [string].
text/xml. In case of y, returns an XML [string].
To access the descriptions individually, by index:
> (Get-Help foo).returnvalues.returnvalue[0].type.name
text/json. In case of x, returns a JSON [string].
However, given the free-form nature of the descriptions and that they're intended for human consumption, this granular access may not be needed.
That said, using this form returns the text without extra whitespace, so (Get-Help foo).returnvalues.returnvalue.type.name
can be used to return all text without empty lines.
This works analogously for the .INPUTS
sections:
(Get-Help foo).inputTypes.inputType.type.name
One neat way is using the PowerShell help comment syntax.
<#
.Synopsis
Returns a list of files.
.Description
...
.Inputs
text/csv
.Outputs
text/json[]
#>
You can access this information from the Get-Help
object:
$cmd = Get-Help -Name .\components\FileList.ps1
"Input Type: " + $cmd.inputTypes.inputType[0].type.name
"Output Type: " + $cmd.returnValues.returnValue[0].type.name
Results in:
Input Type: text/csv
Output Type: text/json[]
Also works on standard cmdlets:
(Get-Help Get-Date).returnValues[0].returnValue[0].type.name
Returns:
System.DateTime or System.String