JavaScript’s Next Tricks: Pipes and Beyond
A practical tour of the Pipeline Operator and the TC39 proposals that are quietly shaping how we’ll write JavaScript next.
Learn the JavaScript Pipeline Operator and key TC39 proposals — Temporal, decorators, import attributes, iterator helpers — plus what’s safe to adopt now.
Let’s be real: JavaScript doesn’t “change” so much as it slowly, politely evolves… and then one day your codebase wakes up and half your patterns feel vintage.
That’s the job of TC39 (the committee that standardizes ECMAScript): take ideas from the wild, argue about them in public, iterate, and (sometimes) ship them. Proposals move through defined stages before they become part of the spec.
In this piece, we’ll unpack the Pipeline Operator — the one everyone talks about — and then zoom out to the other proposals that actually matter in day-to-day code. Not as hype. As “what does it do, why does it exist, and should I care this quarter?”
How TC39 proposals become “real JavaScript”
Before we talk syntax, it helps to understand the pipeline the pipeline proposal lives inside.
Idea → Stage 1 → Stage 2 → Stage 3 → Stage 4 → Included in the spec snapshot → Shipped broadly
(shape) (draft) (complete) (done)Stage labels are not vibes; they’re process milestones. And yes, things can stall for years… or get withdrawn entirely.
A quick cautionary tale: Records & Tuples
If you remember excitement around immutable value types (#{} / #[] style syntax), you also remember the plot twist: Records and Tuples were withdrawn after TC39 couldn’t reach consensus, largely due to concerns like performance expectations.
That’s not drama. That’s the point of the process.
The Pipeline Operator (|>): what it is, and why it’s still not here
The Pipeline Operator proposal aims to make nested expressions readable by turning them into a left-to-right flow.
Today we write:
const result = normalize(trim(toString(input)));Pipes want:
const result = input
|> toString(%)
|> trim(%)
|> normalize(%);The important detail: it’s “Hack-style” pipes (with a topic placeholder)
The current draft spec describes a Hack-style pipe operator that uses a topic reference token (shown as % in the draft) inside the pipe body. The token is explicitly not final—it could change.
Also: this proposal is Stage 2 (Draft) in the TC39 process.
Why does it need a placeholder at all?
Because JavaScript expressions aren’t all unary function calls. Sometimes you want to do:
- property access
- operators
await- multi-arg calls
Hack-style pipes say: “You can write normal expressions, just mark where the piped value goes.”
That’s why this is valid in the draft model:
value |> foo(%, 123) |> %.bar…and why the pipe body must actually reference the topic value (to avoid accidental mistakes).
“But I thought pipes were like value |> fn”
You’re thinking of the F#-style pipe mental model: the RHS evaluates to a unary function and is called with the LHS. TC39 has debated both, and the pipeline proposal docs explicitly note that advancing F#-style pipes has faced strong resistance.
A practical example (today vs proposed)
Working JavaScript today:
const users = await fetch("/api/users").then(r => r.json());
const emails = users
.filter(u => u.active)
.map(u => u.email.trim().toLowerCase());Proposed pipeline-style (conceptual, not broadly runnable without transforms):
const emails =
await fetch("/api/users")
|> await %.json()
|> %.filter(u => u.active)
|> %.map(u => u.email.trim().toLowerCase());The win is readability when your “flow” includes more than simple function calls. The cost is: new syntax, new precedence rules, and bikeshedding the placeholder token. (That last one is weirdly real.)
The proposals that will change your everyday code sooner
Here’s the part people skip: while the Pipeline Operator is the celebrity, several other proposals are already done or close — and they change real projects.
Iterator Helpers: lazy, chainable iteration (Stage 4)
Iterator Helpers add map, filter, take, etc. directly on iterators, enabling lazy pipelines that don’t allocate intermediate arrays. This proposal is Stage 4.
Get Bhagya Rana’s stories in your inbox
Join Medium for free to get updates from this writer.
Conceptually:
// Imagine an iterator over a huge dataset:
const iter = someHugeIterable[Symbol.iterator]();
// Lazy transformation:
const firstTen = iter
.map(x => x * 2)
.filter(x => x % 3 === 0)
.take(10)
.toArray();This is the kind of feature that quietly makes performance and memory usage better — without you doing anything heroic.
Import Attributes: safer, explicit non-JS imports (Stage 4)
Import Attributes (formerly Import Assertions) let you attach metadata to imports — especially useful for things like JSON modules.
Example syntax (host support varies):
import config from "./config.json" with { type: "json" };
console.log(config.featureFlags);If you’ve ever wondered “why is importing JSON different between environments?”, this proposal is basically TC39 trying to standardize that story.
Temporal: modern dates and time zones (Stage 3)
JavaScript’s Date is… a survivor. Temporal is the “let’s fix time” proposal, providing modern date/time types and better time zone handling.
Example feel:
// Conceptual example (exact API depends on runtime/polyfill availability):
const now = Temporal.Now.zonedDateTimeISO("Asia/Kolkata");
const nextWeek = now.add({ days: 7 });
console.log(nextWeek.toString());Even if you don’t use it today, Temporal is the clearest “future default” replacement for most Date pain.
Decorators: structured meta-programming for classes (Stage 3)
Decorators extend classes with a standardized @decorator model.
A tiny example (illustrative):
function sealed(value, context) {
context.addInitializer(function () {
Object.seal(this);
});
}
class User {
@sealed
profile = {};
}If you’ve used decorators in TypeScript or Babel ecosystems, this is TC39 trying to bring order to that universe.
“Emerging” proposals you should watch — but not bet your sprint on
Some proposals are exciting, but still early enough that syntax and semantics can change meaningfully.
Type Annotations in JavaScript (Stage 1)
This proposal adds type syntax that engines ignore at runtime (types as comments). The dream: run a typed superset without transpiling (within a constrained subset). The reality: it’s early, politically complex, and ecosystem-sensitive.
Pattern Matching (Stage 1)
Native pattern matching is one of those features developers want… until you try to standardize syntax everyone agrees on. If it lands someday, it could reduce if/else pyramids and brittle switch logic. But today? Treat it as a horizon feature.
Extractors (Stage 1)
Extractors aim to extend destructuring with custom “match/unpack” behavior. This is powerful… and also the kind of feature that can explode complexity if it’s not shaped carefully.
A simple adoption rule that won’t betray you
Here’s the “architecture flow” I use when deciding whether a proposal belongs in production code:
Need it now?
├─ No → Learn it casually, wait for Stage 4 + broad runtime support
└─ Yes
├─ Is it Stage 4? → Consider using (verify runtime support)
├─ Is it Stage 3? → Use only with strong guardrails (polyfill/compile, tests, fallback)
└─ Stage 1/2 → Treat as experimental (avoid coupling core architecture to it)And one more truth: stage ≠ shipped everywhere. Stage 4 means “finished in the standardization sense,” not “every environment you care about supports it tomorrow.” (You might be wondering why that mismatch exists. It’s because standards and implementations move on different schedules.)
Conclusion: Pipes are the headline, but not the whole story
The Pipeline Operator is compelling because it speaks to how humans read code: left to right, step by step. But it’s still Stage 2, and the details (including the placeholder token) aren’t final.
Meanwhile, the proposals most likely to reshape your everyday JavaScript are the quieter ones:
- Iterator Helpers (done)
- Import Attributes (done)
- Temporal and Decorators (close, but not final)
If you’re building for the long term, the move is simple: track what’s Stage 3/4, prototype carefully, and don’t architect your core system around syntax that might get rewritten or withdrawn (Records & Tuples say hi).
CTA: If you’ve experimented with any of these proposals — especially pipelines — drop a comment with what felt great and what felt… surprisingly awkward. And if you want more TC39 breakdowns like this, follow along.








