How Upcoming TC39 Proposals Could Transform JavaScript Function Calls
This article examines five active TC39 proposals—Pipeline Operator, Function Pipe/Flow, Partial Application, Call This, and Extensions—explaining their design goals, differences, and how they aim to improve function invocation readability, dataflow programming, method extension, and partial application in future ECMAScript versions.
Recently, TC39 is discussing five proposals that aim to improve how functions (methods) are called in ECMAScript, enhancing readability and expressive power.
Pipeline Operator (proposal-pipeline-operator)
Function Pipe/Flow (proposal-function-pipe-flow)
Partial Application (proposal-partial-application)
Call This (proposal-call-this)
Extensions (proposal-extensions)
Because ECMAScript is the foundation of the Web, introducing new syntax is difficult; proposals must solve real problems without crowding future language design space. The five proposals overlap, so TC39 seeks a single solution that covers most use‑cases.
Dataflow Programming
Dataflow programming splits a program into independent units and lets data flow between them like a directed graph. It is common in languages such as Verilog and frameworks like Apache Flink or TensorFlow. In JavaScript, functions are first‑class citizens, enabling functional patterns such as composition and currying.
The Pipeline Operator and Function Pipe/Flow proposals do not add dataflow concepts themselves; they focus on a more expressive way to pass data between functions, allowing developers to write programs as independent black‑box units.
Pipeline Operator
Proposal address: https://github.com/tc39/proposal-pipeline-operator
Before the pipeline operator, developers used nested calls (e.g., f3(f2(f1(val)))) or method chaining (e.g., val.f1().f2().f3()), both of which have readability drawbacks.
Nested calls are hard to read from right to left, especially when intermediate functions require extra arguments. They remain the most generic form because they allow insertion of await, yield, etc.
Method chaining reads left‑to‑right but requires each step to return the object (often via return this) and limits usage to methods defined on the object.
Method chaining corresponds to the Fluent Interface design pattern.
A third style declares a temporary variable for each step:
const result1 = f1(value, arg1);
const result2 = f2(result1, arg2);
const result3 = f3(result2);Using a single temporary variable can cause subtle bugs in asynchronous or closure contexts.
With the pipeline operator, the previous code becomes:
let _ = one()
|> double(%)
|> Promise.resolve().then(() => {
// prints 2
console.log(%);
});
_ = one();The placeholder % represents the previous step's result; the operator |> can be followed by any expression, such as |> await % or |> %.foo().
Each pipeline stage must consume the previous value (the % placeholder must appear).
Because |> shares precedence with arrow functions and ternary operators, parentheses are required to disambiguate complex expressions.
The placeholder cannot be used inside dynamically evaluated code like eval(' % + 1').
Function Pipe/Flow
Proposal address: https://github.com/js-choi/proposal-function-pipe-flow
This proposal adds four methods to the built‑in Function object: pipe, pipeAsync, flow, and flowAsync.
const { pipe } = Function;
pipe(5, f0, f1, f2); // equivalent to f2(f1(f0(5)))
pipe(); // returns undefined pipeAsyncautomatically wraps the first argument with Promise.resolve and chains the functions with .then:
const { pipeAsync } = Function;
pipeAsync(5, f0, f1, f2); // equivalent to Promise.resolve(5).then(f0).then(f1).then(f2) flowreturns a new higher‑order function that composes the supplied functions without invoking them immediately:
const { flow } = Function;
const f = flow(f0, f1, f2); // f(...args) => f2(f1(f0(...args))) flowAsynccreates an async composition:
const { flowAsync } = Function;
const f = flowAsync(f0, f1, f2); // async (...args) => await f2(await f1(await f0(...args)))These methods provide a concise alternative to the pipeline operator for one‑argument functions.
Call This
Proposal address: https://github.com/tc39/proposal-call-this
The :> operator expresses fn.call(receiver, …args) in a more readable way:
receiver :> fn(arg0, arg1) // fn.call(receiver, arg0, arg1)
receiver :> ns.fn(arg0, arg1) // ns.fn.call(receiver, arg0, arg1)
receiver :> (expr)(arg0, arg1) // (expr).call(receiver, arg0, arg1)It can only be used in call expressions, not for creating a bound this value.
Extensions
Proposal address: https://github.com/tc39/proposal-extensions
The :: operator introduces true extension methods. Functions or property descriptors must first be assigned to a separate ::name namespace:
const ::toArray = function() { return [...this]; };
const ::toSet = function() { return new Set(this); };
const result1 = new Set([1,2,3])::toArray(); // [1,2,3]
const result2 = new Array([1,2,3,3])::toArray(); // [1,2,3,3]Extension methods can also be used on built‑in prototypes:
const ::flatMap = Array.prototype.flatMap;
let classCount = document.querySelectorAll('div')::flatMap(e => e.classList::toArray());Property accessors can be extended as well:
const ::last = {
get() { return this[this.length - 1]; },
set(v) { this[this.length - 1] = v; }
};
let a = [1,2,3];
a::last; // 3
a::last++; // modifies array to [1,2,4]The proposal also shows how to create a generic ::pipe extension for chaining arbitrary functions.
Partial Function Application (PFA)
Proposal address: https://github.com/tc39/proposal-partial-application PFA introduces a new call syntax using ~() and the placeholder ? (or ordered placeholders like ?0 , ?1 ) to fix some arguments while leaving others for later application:
const add = (x, y) => x + y;
const addOne = add~(1, ?);
addOne(2); // 3Variadic arguments can be captured with ... :
const logger = console.log~('[service]', ...);
logger('foo', 'bar'); // prints: [service] foo barPFA aims to replace Function.prototype.bind with a more expressive syntax that works with all valid call expressions, including optional chaining.
Summary
All five proposals share the goal of improving function invocation in ECMAScript: the pipeline operator and Function Pipe/Flow bring data‑flow style composition; Call This and Extensions replace .call and method‑extension patterns; Partial Application supersedes bind . Their eventual adoption will reshape how developers write and reason about JavaScript functions.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
