From 24598b92f4c203574f3adab143e2fd22055d39c1 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 25 Jul 2026 14:14:34 +0200 Subject: [PATCH 1/2] Add OutputType parameter set scoping and strengthen parameter set naming rules - Add '[OutputType] and parameter sets' subsection after the Section structure code example, covering single-type (no scoping) vs per-set scoping with a concrete powershell snippet and the OutputType/.OUTPUTS contract rule. - Replace the weak parameter set naming bullet with a full rule: names appear verbatim in Get-Help syntax output, must be readable English phrases (ByName, ByPath, WithCredential, AsSession style), lists explicitly unacceptable names, and mandates DefaultParameterSetName when multiple sets exist. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Coding-Standards/PowerShell/Functions.md | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/docs/Coding-Standards/PowerShell/Functions.md b/src/docs/Coding-Standards/PowerShell/Functions.md index 1f76f67..6e76619 100644 --- a/src/docs/Coding-Standards/PowerShell/Functions.md +++ b/src/docs/Coding-Standards/PowerShell/Functions.md @@ -51,6 +51,25 @@ function Get-UserData { } ``` +### `[OutputType]` and parameter sets + +When every parameter set returns the same type, one `[OutputType]` with no scoping is correct. When different parameter sets return different types, scope each `[OutputType]` to its parameter set using the `ParameterSetName` argument — `[OutputType]` may appear multiple times on the same function, once per type/set combination: + +```powershell +[OutputType([System.String], ParameterSetName = 'ByName')] +[OutputType([System.IO.FileInfo], ParameterSetName = 'ByPath')] +[CmdletBinding(DefaultParameterSetName = 'ByName')] +param( + [Parameter(Mandatory, ParameterSetName = 'ByName')] + [string] $Name, + + [Parameter(Mandatory, ParameterSetName = 'ByPath')] + [string] $Path +) +``` + +The types listed in `[OutputType]` must match what `.OUTPUTS` documents in the comment-based help — they are the contract between the function and its callers. + ## Parameters - **Type every parameter** and validate at the boundary — `[Parameter(Mandatory)]`, `[ValidateSet(...)]`, `[ValidateNotNullOrEmpty()]` — so bad input is rejected early, not deep in the call stack. @@ -58,7 +77,7 @@ function Get-UserData { - **Attribute order**, each on its own line: `[Parameter()]`, then validation attributes, then `[ArgumentCompleter()]`, then `[Alias()]`, then the typed declaration. - **Separate parameters with a blank line**, so each one's inline doc comment, attributes, and typed declaration read as a single block. - **`[switch]` for boolean flags** — never a `[bool]` parameter. -- **Name every parameter set** with an intent-revealing name when a function has more than one mode; never `Default` or `__AllParameterSets`. Set `DefaultParameterSetName` to the most common intent. +- **Name every parameter set** with a meaningful English phrase that describes the *scenario or input strategy* the caller is using — parameter set names appear verbatim in `Get-Help` syntax output and must be prose a caller can read without decoding. Names that describe what the caller provides (`ByName`, `ByPath`, `ByLiteralPath`, `ByGuid`) or how the call is made (`WithCredential`, `AsComputerName`, `AsSession`) work well. Never acceptable: `Default`, `__AllParameterSets`, `__DefaultParameterSet`, `ParameterSetA`, `Set1`, `Mode1`, or any name a caller would have to decode. `DefaultParameterSetName` on `[CmdletBinding()]` must name one of the declared parameter sets using the same prose convention and is never omitted when multiple sets exist. ## State changes and the pipeline From d76bfee6f061d79526181b8a8094f1a516dfaaba Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 25 Jul 2026 14:42:40 +0200 Subject: [PATCH 2/2] Prefer spaced scenario names for parameter sets in examples and rule Update the [OutputType] code example to use 'By display name' and 'From file path' instead of 'ByName'/'ByPath', demonstrating that PowerShell parameter set names support spaces and read more naturally in Get-Help output that way. Strengthen the naming rule bullet to lead with spaced prose names as the preferred form ('By display name', 'From file path', 'As computer name') and note explicitly that spaces are supported and encouraged. PascalCase single-word names remain acceptable as a fallback. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Coding-Standards/PowerShell/Functions.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/docs/Coding-Standards/PowerShell/Functions.md b/src/docs/Coding-Standards/PowerShell/Functions.md index 6e76619..d7fe4a8 100644 --- a/src/docs/Coding-Standards/PowerShell/Functions.md +++ b/src/docs/Coding-Standards/PowerShell/Functions.md @@ -56,15 +56,15 @@ function Get-UserData { When every parameter set returns the same type, one `[OutputType]` with no scoping is correct. When different parameter sets return different types, scope each `[OutputType]` to its parameter set using the `ParameterSetName` argument — `[OutputType]` may appear multiple times on the same function, once per type/set combination: ```powershell -[OutputType([System.String], ParameterSetName = 'ByName')] -[OutputType([System.IO.FileInfo], ParameterSetName = 'ByPath')] -[CmdletBinding(DefaultParameterSetName = 'ByName')] +[OutputType([System.String], ParameterSetName = 'By display name')] +[OutputType([System.IO.FileInfo], ParameterSetName = 'From file path')] +[CmdletBinding(DefaultParameterSetName = 'By display name')] param( - [Parameter(Mandatory, ParameterSetName = 'ByName')] - [string] $Name, + [Parameter(Mandatory, ParameterSetName = 'By display name')] + [string] $DisplayName, - [Parameter(Mandatory, ParameterSetName = 'ByPath')] - [string] $Path + [Parameter(Mandatory, ParameterSetName = 'From file path')] + [string] $FilePath ) ``` @@ -77,7 +77,7 @@ The types listed in `[OutputType]` must match what `.OUTPUTS` documents in the c - **Attribute order**, each on its own line: `[Parameter()]`, then validation attributes, then `[ArgumentCompleter()]`, then `[Alias()]`, then the typed declaration. - **Separate parameters with a blank line**, so each one's inline doc comment, attributes, and typed declaration read as a single block. - **`[switch]` for boolean flags** — never a `[bool]` parameter. -- **Name every parameter set** with a meaningful English phrase that describes the *scenario or input strategy* the caller is using — parameter set names appear verbatim in `Get-Help` syntax output and must be prose a caller can read without decoding. Names that describe what the caller provides (`ByName`, `ByPath`, `ByLiteralPath`, `ByGuid`) or how the call is made (`WithCredential`, `AsComputerName`, `AsSession`) work well. Never acceptable: `Default`, `__AllParameterSets`, `__DefaultParameterSet`, `ParameterSetA`, `Set1`, `Mode1`, or any name a caller would have to decode. `DefaultParameterSetName` on `[CmdletBinding()]` must name one of the declared parameter sets using the same prose convention and is never omitted when multiple sets exist. +- **Name every parameter set** with a prose phrase that states the caller's scenario — parameter set names appear verbatim in `Get-Help` syntax output and must read naturally to anyone calling the command. **Spaces are supported** and encouraged: `'By display name'`, `'From file path'`, `'As computer name'`, `'With credential'`, `'As session'`. Single-word PascalCase names (`ByName`, `ByPath`, `ByGuid`) are acceptable when the scenario is self-evident, but multi-word names with spaces read even better in help output. Never acceptable: `Default`, `__AllParameterSets`, `__DefaultParameterSet`, `ParameterSetA`, `Set1`, `Mode1`, or any name a caller would have to decode. `DefaultParameterSetName` on `[CmdletBinding()]` must name one of the declared parameter sets using the same prose convention and is never omitted when multiple sets exist. ## State changes and the pipeline