November 14, 2024
Three Game-Changing JavaScript Proposals You Should Know About
Table of contents
JavaScript is evolving, and the future looks exciting! While you’re writing your everyday if
statements and wrestling with Date objects, the TC39 members are cooking up some game-changing features that could revolutionize how we write JavaScript. From elegant pattern matching to intuitive date handling, let’s dive into three proposals that might just make your developer life a whole lot better.
ECMAScript
ECMAScript is the standardized specification for JavaScript - it’s essentially the blueprint that defines how the language should work. While JavaScript is the implementation, ECMAScript is the standard that describes the rules and features of the language.
Every year - and more precisely every June - the ECMAScript committee publishes a specification document that describes the most recent yearly snapshot plus any finished proposals since the snapshot was taken
.
Today, we will focus on the TC39 (Technical Committee 39) - the committee responsible for evolving ECMAScript. It consists of various stakeholders including browser vendors, academics, and other tech companies (IBM, Meta, Shopify, etc.). They follow a specific process for adding new features to the standard through proposals.
The proposal process
For a new feature to be added to the standard, it has to go through multiple stages. Indeed, JavaScript is used by millions (or billions) of people and it’s important to make sure that new features are stable and well-thought-out.
Currently, 98.3% of all websites worldwide use JavaScript, which emphasizes the importance of ensuring new features are stable before major browsers implement them.
To do so, the TC39 committee has established a 6 stages process.
Stage 0: Strawperson
This is where it all starts. Proposals can be submitted by anyone who’s part of TC39 or by an external contributor who has registered as a TC39 contributor.
Then, the proposal has to be presented at a committee meeting. After that, it will officially become a Stage 0 proposal and will be listed here.
Stage 1: Proposal
This is where the proposal gets more serious. What they call a champion
and/or a co-champion
need to be identified. They are authors and editors of the proposal and are responsible for advancing it through the process.
Champions have to write down the problem solved by the proposal, a high-level overview of the solution (via examples), and a detailed specification of the new feature (API specification and semantics details). This is also where potential challenges are identified.
Stage 2: Draft
Here we go! A preferred solution has been chosen by the committee. Now, the committee expects the solution to be implemented. As it’s still a draft, the specification is not final and can still be changed but there are good chances that the feature will be added to the standard.
At this stage, experimental implementations (such as polyfills) are encouraged to help validate the design, but they don’t need to be production-ready implementations yet.
Stage 2.7 (new stage officially added in 2023)
As this is a relatively new stage, I’ll let Rob Palmer (co-chair of TC39) explain it better:
Stage 2.7 is equivalent to what we used to call Stage 3. It means that the design is considered complete, we have a full specification, and we need to write code (tests and non-polyfill implementations) to gain feedback and make progress. It's a strong signal 💪
— Rob Palmer (@robpalmer2) February 7, 2024
🤔 Why did we do this?
— Rob Palmer (@robpalmer2) February 7, 2024
We separated out the "Approved in Principle: Spec Ready" stage from the later "Recommended for Implementation: Tests Ready" stage to reduce wasted effort in writing tests before spec stability, whilst also clarifying the test readiness message to engines.
Stage 3: Candidate
At this point, the proposal is mostly finished. At the previous stage, some todos and placeholders could still be left in the specification. Now, the specification needs to be complete. There must be at least two implementations that comply with the specifications.
Being at this stage is a signal sent to JS engines that the proposal is ready to be implemented.
Stage 4: Finished
The specification will now be included in the annual ECMAScript specification document. Quite a journey, isn’t it?
Exploring some proposals
Pattern matching (stage 1)
Pattern matching is likely something you know if you’ve already used languages like Scala
, Rust
or Elixir
for example. Using pattern matching, we can apply custom logic based on the type of the value passed in.
For now, JavaScript’s pattern matching capabilities are limited to string manipulation through regular expressions. The proposed feature would expand pattern matching to work with various data types and structures.
Let’s take an example from the proposal’s documentation:
The code uses a match
expression to inspect the res
object, which represents the server’s response. Each when
clause defines a pattern that the res
object is matched against, and the corresponding block of code is executed if the pattern is satisfied.
- The first pattern
{ status: 200, let body, ...let rest }
matches a successful response (status code 200). It extracts the body of the response and the remaining properties (rest) and passes them to thehandleData
function. - The second pattern
{ const status, destination: let url } and if (300 <= status && status < 400)
matches a redirected response (status codes 300-399). It extracts the status code and the url destination (which is renamed tourl
), and then checks if the status is within the redirect range before calling thehandleRedirect
function. - The third pattern
{ status: 500 } and if (!this.hasRetried)
matches a server error (status code 500). If the request hasn’t been retried yet, it calls theretry
function and sets ahasRetried
flag to prevent infinite retries. - If none of the previous patterns match, the default clause is executed, which calls the
throwSomething
function.
I find it more elegant to read than the old fashioned way, isn’t it?
Other than being more elegant, pattern matching also allows to write more concise code. Let’s take another example from the proposal.
We have this code:
This example is quite simple but we do zero checks here and we just hope that everything will work as expected. What if the user
array has only one element? Or what if it’s not an array at all?
So let’s add some checks:
We now are certain that everything is correct and of the expected shape. Nice, but it’s a lot of code for something that should be simple, don’t you think?
The exact same thing can be done using pattern matching:
Here, the if statement checks if the json
object has a user
property that is an array with two elements.
The pattern matching inside the array checks that the first element is a String and binds it to the name variable, and the second element is a Number and binds it to the age variable.
If the pattern matching succeeds, we then print the message.
If you’re new to pattern matching, I hope you can see how powerful it can be.
Pipeline operator (stage 2)
When dealing with JavaScript, we often find ourselves writing code that chains multiple function calls together to perform complex operations. This leads to code that is very hard to understand and maintain.
Let’s take an example:
But we can also chain the operations using nested function calls:
This gives us two possible syntaxes: method chaining value.one().two().three()
and nested function calls three(two(one(value)))
.
The pipeline operator (|>
) proposes a new syntax that makes the code more readable by allowing us to chain operations from left to right. Here’s how our example would look:
The pipeline operator takes the value on the left and passes it to the function on the right. This creates a clear flow of data transformation that’s much easier to read and understand.
You maybe noticed that we used %
in the example. This is a placeholder for the value that is being passed through. Actually, as this proposal is still in stage 2, there are two approaches proposed to handle this placeholder.
Hack pipes
First one is what we did in the example above. It’s called Hack pipes
.
In the Hack language’s pipe syntax, the righthand side of the pipe is an expression containing a special placeholder, which is evaluated with the placeholder bound to the result of evaluating the lefthand side’s expression. That is, we write value |> one(%) |> two(%) |> three(%) to pipe value through the three functions.
The only downside of this approach is that piping though unary functions is not as straightforward as it could be. Indeed, we can’t just write something like value |> capitalize |> getDomain
for example.
F# pipes
In the F# language’s pipe syntax, the righthand side of the pipe is an expression that must evaluate into a unary function, which is then tacitly called with the lefthand side’s value as its sole argument. That is, we write value |> one |> two |> three to pipe value through the three functions.
Let’s just rewrite the above example using this approach to see how it works:
You can see we no longer need to use the %
placeholder. And we also used curried functions which makes the code even more elegant. Take the split
function:
If you’re not familiar with curried functions, let me give you a quick explanation. Currying is the process of transforming a function that takes multiple arguments into a sequence of functions that each take a single argument.
This is useful here because it allows us to call split('@')
and get a function that takes the value to split as an argument.
The only downside of this approach is that it can be a little bit more verbose in some cases. Imagine we want to call a function that takes more than one argument (so not a curried function). We would have to write it like this:
Instead of:
For now, there is no consensus even though the TC39 committee has rejected the F# pipes approach multiple times because of a variety of reasons - memory performance concerns and so on.
If you want to know more about that, do not hesitate to read the proposal’s documentation. And if you want to play with it, you can use the Babel plugin.
Without taking into account any technical concerns, I personally think the F# pipes approach is more elegant and would result in a less verbose code in the majority of cases.
Temporal (stage 3)
Temporal’s proposal provides standard objects and functions for working with dates and times. If you’ve ever worked with dates in JavaScript, you know that the current Date
API has many limitations and quirks:
- It’s mutable (dates can be modified in place)
- Month numbers are zero-based (January is 0)
- No support for different calendar systems
- Confusing parsing behavior
The Temporal API aims to solve these issues by providing a modern, more intuitive way to work with dates and times.
Let’s explore some key features.
Separate Types for Different Use Cases
Timezone Support
One of the most powerful features is the first-class support for timezones:
Duration Calculations
Temporal makes it easy to work with durations and perform date arithmetic:
Calendar Support
Unlike the current Date API, Temporal supports non-Gregorian calendars:
As you can see, the Temporal API represents a significant improvement over the current Date API, offering a more intuitive and powerful way to work with dates and time. Its immutable design, first-class timezone support, and clear separation of concerns make it a welcome addition to the standard.
If you want to know more, you can read the proposal’s documentation. And if you want to play with it, you can use this polyfill.
Conclusion
While we’ve explored only three proposals, there are many more exciting ones in development! If you want to have a look, I recommend you this website that is not the official one but lists all the proposals and their status. Here is a link to the official one.
The JavaScript ecosystem continues to evolve with exciting new proposals that aim to make the language more powerful, expressive, and developer-friendly. From pattern matching that simplifies complex conditional logic, to the pipeline operator that makes function composition more readable, to the Temporal API that finally brings robust date/time handling to JavaScript - these proposals represent significant improvements to the language.
While the TC39 process may seem lengthy, it ensures that new features are thoroughly vetted and well-designed before becoming part of the language specification.
As these proposals progress through the various stages, developers can already start experimenting with many of them using polyfills or transpilers. This not only helps validate the proposals but also allows the community to provide valuable feedback that shapes the future of JavaScript.