From a4f43083fb154a38020fa55b7091fe55804487ba Mon Sep 17 00:00:00 2001 From: Ubugeeei Date: Thu, 5 Mar 2026 20:21:13 +0900 Subject: [PATCH] feat: patterned templates --- active-rfcs/0046-patterned-template.md | 356 +++++++++++++++++++++++++ 1 file changed, 356 insertions(+) create mode 100644 active-rfcs/0046-patterned-template.md diff --git a/active-rfcs/0046-patterned-template.md b/active-rfcs/0046-patterned-template.md new file mode 100644 index 00000000..5c256a20 --- /dev/null +++ b/active-rfcs/0046-patterned-template.md @@ -0,0 +1,356 @@ +- Start Date: 2026-03-05 +- Target Major Version: 3.x +- Reference Issues: N/A +- Implementation PR: (leave this empty) + +# Summary + +Introduce a pattern matching directive for Vue templates that enables declarative, multi-branch conditional rendering based on the value of an expression. This provides a cleaner alternative to chained `v-if` / `v-else-if` / `v-else` when branching on a single expression's value. + +# Basic example + +```html + +``` + +This is equivalent to: + +```html + +``` + +# Motivation + +## The problem with chained `v-if` / `v-else-if` + +When rendering different content based on the value of a single reactive expression, developers must repeatedly reference the same expression in a chain of `v-if` / `v-else-if` directives: + +```html + +``` + +This pattern has several drawbacks: + +1. **Repetition**: The discriminant expression (`result.tag`) is repeated in every branch, violating DRY and increasing the chance of typos. +2. **Readability**: It is hard to see at a glance that all branches are testing the same expression. +3. **Fragility**: Adding or reordering branches requires careful attention to the `v-else-if` chain. +4. **Lack of intent**: The `v-if` chain doesn't communicate that this is exhaustive matching on a single value — it looks the same as unrelated conditions. + +## Growing need for discriminated unions in Vue + +TypeScript's discriminated unions and tagged union patterns are widely adopted in Vue 3 codebases. Libraries like VueQuery, Pinia, and vue-router commonly return status objects (e.g., `{ status: 'loading' | 'error' | 'success', ... }`). A dedicated pattern matching directive makes it natural to consume these patterns in templates. + +## Prior art + +- **JavaScript TC39 Pattern Matching Proposal** (Stage 1): `match (expr) { when ... }` +- **Svelte**: `{#if}` / `{:else if}` chains (no dedicated switch) +- **Rust**: `match` expression +- **Swift**: `switch` statement with pattern matching +- **Kotlin**: `when` expression + +# Detailed design + +## Syntax candidates + +We propose several syntax candidates. The naming of the directive pair is a key design decision. + +### Option A: `v-match` / `v-case` (Recommended) + +```html +
+

...

+

...

+

...

+
+``` + +- `v-match` communicates "pattern matching" intent, aligning with TC39 proposal and Rust naming. +- `v-case` is familiar from `switch/case` in JavaScript. +- `.default` modifier for the fallback branch. + +### Option B: `v-switch` / `v-case` + +```html +
+

...

+

...

+

...

+
+``` + +- Most familiar to JavaScript developers (`switch/case`). +- However, `v-switch` may imply fall-through semantics, which this directive does **not** have. + +### Option C: `v-match` / `v-when` + +```html +
+

...

+

...

+

...

+
+``` + +- Aligns with TC39 `match/when` and Kotlin's `when`. +- Reads naturally: "match expression — when value1, when value2." + +### Option D: `v-pattern` / `v-case` + +```html +
+

...

+

...

+

...

+
+``` + +- Explicit about the "pattern matching" concept. +- Slightly more verbose. + +### Recommendation + +**Option A (`v-match` / `v-case`)** is the recommended syntax because: + +- `match` is concise and well-recognized across languages as a pattern matching keyword. +- `case` is universally understood as a branch within a match/switch. +- No fall-through ambiguity (unlike `v-switch`). +- Aligns with the likely direction of the TC39 pattern matching proposal. + +## Semantics + +### Basic matching (strict equality) + +`v-match` evaluates its expression once per render. Each `v-case` child is compared against the result using strict equality (`===`). Only the first matching `v-case` branch is rendered. + +```html + +``` + +### Using `