
Future JavaScript: what is still missing? - scottfr
http://2ality.com/2019/01/future-js.html
======
burlesona
There are some nice ideas here, but how many different ways do you need to do
something? Since JavaScript has an especially high burden for backward
compatibility you can’t really remove old cruft, and adding yet another way to
(whatever) just makes the language harder to learn and harder to read.

Warts and all, one of the best things about ES5 and earlier is that the
language was so simple that you could pick it up in a day.

The later additions have added developer convenience for sure, but at the
expense of taking something simple, consistent, and very easy to read, and
turning it into a complex and subtle language with a lot of redundant syntax.
For example all the different syntaxes for defining a function with the subtle
differences between them. And in this articles proposal - since switch doesn’t
do what I want, we’ll have both switch AND case with different semantics and
syntax. Ugh.

At this point I’d rather see an effort to deprecate cruft or standardize
features over throwing in new things.

~~~
scarface74
There was nothing easy to read about the callback hell that resulted in the
asynchronous nature of JavaScript.

Promises made it better but that was still verbose. Then async/await was added
that made code read more linearly.

There was also the unintuitiveness of how var declarations were always
function scoped compared to how most developers would think that variables
should be blocked scoped using let and const.

Also I understand how prototypical inheritance works and I know that the class
syntax is just syntactic sugar, but it is a lot more readable.

~~~
ChrisSD
Improving some of the syntax was sorely needed but I think adding classes was
unhelpful. I already have a hard enough time teaching programmers about
prototypical inheritance without the language actively lying to them.

And it feels like an odd move in that class based OOP isn't the zeitgeist it
once was. Shoehorning it in feels like a throw back to 90's and 00's.

~~~
munificent
_> I already have a hard enough time teaching programmers about prototypical
inheritance without the language actively lying to them._

One way to look at it is that you could consider _not_ teaching the underlying
prototypal inheritance model at all. If students will rarely run into code
that relies on it, it may be vestigial semantics.

Think of it like how people used to teach patterns in C for doing object-
oriented programming. For example, you could have a struct whose first field
was a pointer to a table of functions for the operations you could perform on
the object. In other words, how to implement your own vtables from scratch.

When C++ came along, it was still useful to understand vtables sometimes, but
it wasn't something every programmer needed to fully internalize. C++ let them
program at a higher level of abstraction.

 _> And it feels like an odd move in that class based OOP isn't the zeitgeist
it once was._

Citation needed. :) Two of the newest, fastest growing languages are class-
based: Kotlin and Swift. I'm not aware of any new prototype-based language
that has noticeable growth.

Prototypes are a neat idea, but I think the market has pretty clearly shown
that classes are a better practical approach to OOP.

~~~
danShumway
I'm skeptical that classes make prototypal inheritance safe to ignore, because
they're not a perfect abstraction. Opinion me, classes in Javascript do not
act like classes in other langagues.

In particular, I still run into usage of `call` and `apply` on a regular
basis.

    
    
      class Foo {
         constructor () { this.foo = 0; }
         log () { return this.foo; }
       }
    
      (new Foo()).log.call({ foo: 2 });
    

I guess I can explain the above without prototype, but it's certainly weird
behavior for a classical OOP language, and at the very least it's dancing
around prototype-like behavior.

Or, much worse:

    
    
      class Bar { constructor () { return new Foo(); } }
    

And you can say, "how often is a student ever going to run into that case",
but part of being a student is experimenting with code. If an abstraction only
holds as long as you never do something weird, then it's probably not a very
good abstraction. In that case it's worth asking, "is it good language design
to have a feature give surprising results as soon as you do something that's
even slightly off of the beaten path?"

~~~
scarface74
Well, in that case the abstraction of private methods isn’t good either
because there are ways to bypass them in C++ by passing a function pointer or
in C# via reflection.

~~~
danShumway
Arguably, yes.

However, I would say there's some difference in severity between a surprising
behavior that results from the intersection between two features, and a
surprising behavior that results because the way a feature is described is
literally inaccurate.

You can break Javascript classes without using any other feature of the
language. You don't have to resort to something like Proxies, or overriding
native prototypes. This is because it's not that other parts of Javascript
provide escape hatches to get around behavior, it's that the class syntax
itself does not cover the entirety of the prototype system it is trying to
obscure.

It's not ideal to have any breakage in an abstraction, but some breaks are
worse than others. To me, edge cases like this:

    
    
      let foo = function () { this.x = 5; }
      class Bar extends foo { //No error? But why?
        constructor () { super(); }
      }
    
      foo = function () { this.x = 7; }
      console.log((new Bar()).x); // 5, not 7 or undefined.
    

are on a whole nother level of leaky abstractions over even some of the worst
parts of C#. To me, this is an edge case that nearly any reasonably
imaginative student would likely find at some point using only very basic
components of the Javascript language. And I have no idea how I would even
begin to tackle explaining this without saying, "Okay, throw out everything I
taught you about classes, here's how the language really works."

------
GordonS
I'd like a decent standard library.

These days I work mainly with C#, and to a lesser extent Typescript and
JavaScript - the anemic nature of the ECMAScript standard library is really
jarring.

~~~
dehrmann
I mostly work with Java and Python. Whenever I use Javascript, the lack of
basic data structures other that arrays and objects is always jarring.

~~~
hajile
Objects, Arrays/lists (technically objects), TypedArrays, Set, Map (with weak
variants). What other important structures did you have in mind?

~~~
dehrmann
ArrayDeque, LinkedHashMap, TreeMap, PriorityQueue, even LinkedList. Their
interfaces have gotten pretty built-out, too, with methods like
computeIfAbsent.

It also has a rich collection of concurrent data structures, but those aren't
useful in Javascript (yet).

~~~
hajile
Lucky for you, JS already has good solutions for most of those things.

LinkedList is almost indistinguishable from an growable array. Since the JS
array grows by default, you're covered for the kinds of things you'd want a
linked list for (in fact, most older JS implementations actually implemented
arrays as linked lists).

ArrayDeque's big claim to fame is add/remove from both ends. That's also
covered by the standard array (shift/unshift, pop/push).

Linked hashmap's big feature is maintaining insertion order. The JS Map type
does this automatically. JS Objects don't do this officially, but since so
much legacy code depends on this "feature", it is implemented for objects
across all major browsers.

Priority Queue isn't in JS, but I'd argue it's rather niche and rather few
async frontend JS projects need it. On the backend, one extra import is hardly
a big deal.

There is also no treemap. They are simple to implement (esp with proxies). JS
objects store all keys as strings and `Object.keys(foo).sort()` is probably
about as fast. As an additional issue, JS maps can have keys of different
types, so deciding how to perform that sort would be very difficult. This also
seems like a niche issue that belongs outside the standard library.

~~~
int_19h
> LinkedList is almost indistinguishable from an growable array. Since the JS
> array grows by default, you're covered for the kinds of things you'd want a
> linked list for (in fact, most older JS implementations actually implemented
> arrays as linked lists).

Not only they have different performance for various operations (append,
prepend, indexing, removal in the middle), but perhaps even more importantly,
linked lists can share data if it's immutable. So an array is not a
replacement.

~~~
hajile
You'd be correct -- if we were talking about a lower-level language.

JS implementations are free to choose whichever array representation works
best be that hashtable, struct, class, linked list, array, or whatever. Modern
JITs use a couple different approaches that have been found to be fastest and
can even switch representations on the fly based on several factors (is the
array homogeneous, is it sparse, is it short, med, or long, and so on).

Even moving away from JS, there should be a healthy skepticism for basic O
notation about data structures and algorithms. When your processor is
thousands or millions of times faster than your ability to send over data from
memory, the notion of what is faster changes a lot. If my linked list misses
in cache because it's memory isn't sequential, then the time it takes to add
an element and read may be much longer than copying an array and expanding it
a few entries (especially if a fill pointer is used).

~~~
int_19h
Are you saying that JS implementations somehow know whether the array is used
to store immutable values, and will switch implementations to allow for
sharing?

~~~
hajile
In JS, primitives are immutable -- number, functions (but not the fn object),
regex, strings, and booleans. All other types are not immutable -- Object,
Array (actually an Object), Map, Set, TypedArray, etc. Even if a linked list
were added, it would not be immutable.

That said, JS has Object.freeze() which can make an object immutable. You
could do this recursively to make a guaranteed immutable object. Immutable
libraries in JS tend to use other methods for performance reasons though IIRC.

------
tobyhinloopen
I may have overlooked it, but I want actual proper integers. Not doubles, just
integers.

I also missed the awesome bind-operator. EG:

    
    
        const getFoo = () => this.foo;
        const bar = { foo: 1 };
        console.log(bar::getFoo()); // 1
    

[https://github.com/tc39/proposal-bind-
operator](https://github.com/tc39/proposal-bind-operator)

~~~
chrisseaton
> I want actual proper integers. Not doubles, just integers.

Why exactly do you want that? Is it arbitrary precision you want? Is the 56
bits or whatever not enough for your use-case? Or do you find the range checks
are too expensive in practice?

~~~
karmakaze
Yes 56 bits is not enough. I want compatibility with database types and any
other protocols I choose to use rather than having a js-crippled subset.

------
danShumway
There's some interesting stuff in here, but I'm noticing a large chunk of it
is syntactic sugar.

Keeping the extensible web manifesto in mind, I wonder if maybe what we really
want is to have some kind of standardization around transpiling code.

With new features like promises, we had extensive testing via user libraries
before they made their way into the language. But with these kinds of
syntactic features, that doesn't seem to be happening.

If a feature is almost purely syntactic sugar, maybe it would make more sense
for it to be supplied via a transpiler rather than being baked into the core
language? Are there pain points or disadvantages to libraries like Babel that
we could address via the core language instead?

~~~
scarface74
Yes it makes the toolchain more complicated and it makes debugging slightly
harder. On top of that having it as a standard means you can go anywhere and
just use it without worrying about which transpiler they are using.

~~~
danShumway
> Yes it makes the toolchain more complicated and it makes debugging slightly
> harder.

Are there ways we can help with this? Improvements we could be making to
sourcemaps? Maybe we should be looking into adding new descriptors that are
designed specifically to help generated code get read by debuggers? Maybe some
kind of support for marking regions of code so you can run multiple
transpilers on the same codebase?

I dislike transpilation in its current form, but I can think of things off the
top of my head that would make it easier.

> On top of that having as a standard means you can go anywhere and just use
> it without worrying about which transpiler they are using.

Fair enough, there are definitely advantages to having a large set of core
features. But if you look at languages like modern C++, it seems that there
are also disadvantages that come along with that.

Given that the web is far more permanent than most other languages (we can
_never_ remove a JS feature), are the advantages of having one code style
worth the disadvantages of not being able to test new syntax in the wild
before we standardize?

There's a balance here -- I like lodash, a lot. But I don't want lodash to be
part of the core language. Maybe some syntax fits into that category as well?

~~~
scarface74
But why not have a larger standard library for Node for instance so you don’t
have so many dependencies and have separate optional libraries for the
browser?

~~~
danShumway
Because [https://medium.com/the-node-js-collection/keeping-the-
node-j...](https://medium.com/the-node-js-collection/keeping-the-node-js-core-
small-137f83d18152)

Note that Node is actually in a much better position than Javascript as a
whole, because Node apps can be shipped with a specific version of Node. This
means that the Node team actually has more power than web browsers to
deprecate old features and remove parts of the library, as long as they do so
carefully. It is much harder to make breaking changes in a browser than it is
to make them in Node.

One middle ground I've seen people suggest is to have officially "blessed"
external libraries that still need to be imported, but that are cached locally
or have special privileges. For example, JQuery was at one point so common
that the Firefox dev tools actually ship with a subset of the JQuery API
enabled -- just so devs that are used to working with it can use it in the
debugger on any site.

I look at JQuery as a massive success story for how 3rd-party extensions
should work. It was extremely ubiquitous, it did influence the core language,
but now we've grown and because it was kept separate, we're still free to
abandon it and move on.

On the Node side maybe that means that Node ships with dependencies that still
need to be added to your package.json, but that are always bundled in your
installation so that you have them locally and don't need to hit a network
request when you type `npm install X`.

Also on the Node side we have modules that are built by the core team, but
just not distributed in the core library. This helps out a bit with security,
because you can still have a large organization like Google maintaining a
feature and validating it, they just ship it separately from their browser.

------
a_lieb
As someone who teaches JS on the side, I'm literally scared of the idea of
introducing another construct called "case." Especially one that goes in the
opposite place of where the "case" normally does: "switch ... case" vs. "case
... when."

JavaScript is already jam packed with subtleties and gotchas that make it hard
to learn, including situations where two things have the same name or the name
doesn't quite match the underlying concept: C-style "for" vs. "for ... in" vs.
"for ... of," object vs. Object, Object.prototype vs. "the prototype of the
object," etc. We don't need another one of those.

I'd actually like this if it used another word than "case." "choose ... when",
maybe? Kinda awkward, but I bet someone could think of something better.
"case" has the nice feature that it's already a reserved word, but it's not
valid syntax to have a variable name right next to an open brace, so the
parsers could figure it out.

------
marcosdumay
Is it just me or are those really making Javascript weirder?

    
    
        const func = do {
          let result;
          () => {
            if (result === undefined) {
              result = someComputation();
            }
            return result;
          };
        };
    

Shouldn't that be "if (result === undefined) then someComputation else
result"? Why import functional style and proceed to make it inherently
imperative with a return in the middle of the block?

    
    
        obj?.foo?.bar?.baz
    

The point of optional chaining is that you can place functions within the
chain. That seems to be a very surprising and useless way to do them.

~~~
seniorsassycat
This is the equivalent without a do expression.

    
    
        const func = (() => {
          let result; // cache
          return () => {
            if (result === undefined) {
              result = someComputation();
            }
            return result;
          }
        })();
    

You could write it without the immediately invoked arrow funciton or a do
expression if `result` is in the same scope as `func`

    
    
        let result;
        const func = () => {
          if (result === undefined) {
            result = someComputation();
          } 
          return result;
        }
    

Result is in the same scope as func, inviting anyone to read from or write to
it.

In all three examples `func` is assigned a zero argument function. That
function returns the result of calling `someComputation`, but it stores the
value in `result`. If `result` is not undefined `func` returns `result`.
`func` is a cached, memoized, or lazy-loaded version of someComputation.

A do expression will return the last evaluated expression in it's scope, in
this example that expression is `() => {...}`, not `return` because the inner
anonymous function wasn't called.

\---

I think a clearer demonstration of do expressions is an assigning switch or
ifelse

    
    
        const value = do {
          switch (someString) {
            case 'Monday':
              0;
              break;
            case 'Tuesday':
              1;
              break;
            ...
          }
        }
    
        const other = do {
          if (isPrime(n)) {
            'prime'
          } else if (isOdd(n)) {
            'odd'
          } else {
            'even'
          }
        }

~~~
marcosdumay
Oh, your examples are better. Except for the break on the functional switch
(that is weird again), they are how it should be done.

Is the example on the article wrong? A functional "if" shouldn't accept just
one side of the branch, a functional switch shouldn't accept a "break", and
although it's not absurd that a functional code block accepts a "return", it
is not usual.

~~~
y4mi
the break is part of a switch/case though?

[https://www.w3schools.com/js/js_switch.asp](https://www.w3schools.com/js/js_switch.asp)

------
Mugwort
Javascript is still missing simplicity, consistency and a unified core set of
ideas. It will always be that way too. Javascript will always be something
grudgingly accepted or used with some misgiving. ES6 has some very nice
features and the good parts of javascript stand out as much as the unfortunate
inconsistencies lurking inside the language. People know how to use it. Some
use it quite well but I always get the feeling I have to step around carefully
and not make a dumb mistake in js which is easier to do than almost any other
language except say PHP. Web assembly should open up some badly needed
possibilities. All we really need is a consistent API. A world without js? I
hope so.

~~~
jdpigeon
I feel like this opinion is too popular so I just want to respectfully speak
my peace. I'm a relatively new developer and I never learned any of the
languages that a lot of the old guard fawns over, but after learning coding
with Racket, Java, and Python, JavaScript (especially TypeScript) has always
felt, and continues to feel, awesome.

~~~
leshow
I don't know what languages you're referring to but I didn't have my eyes
opened until I spent significant amounts of time learning Haskell. I think any
language with suitably advanced type system would do: ocaml/reason, rust,
swift, etc. All the OO c-derived languages feel super bland to me since they
all deliver equivalent expressive power, more or less. You get a really
different experience once you get a language with a more capable type system.

Honestly I'm kind of surprised that Racket didn't stand out to you, it's a
super cool language and very different from everything else on your list. How
much time have you spent using it?

~~~
jdpigeon
Haskell is probably #1 for languages that old guard types fawn over and, yea,
I think it's pretty cool, but don't ever expect to be in a situation where I
get paid to write it.

I really liked Racket a lot actually! It turned me on to the whole functional
programming style that I still try to maintain in JavaScript. Unfortunately, I
only got to write it for a semester in school and when I moved on to
JavaScript enjoyed not having the insanity of all those parentheses and not
having to do everything myself (via js array functions, lodash, and the npm
ecosystem)

~~~
leshow
Maybe there's a reason people keep saying it? I guess you'll never know until
you try it yourself.

> enjoyed not having the insanity of all those commas and not having to do
> everything myself

Racket has a pretty robust std library that includes things like folds, maps,
zips and whatever else you'd be using from lodash. It's also pretty void of
commas, not really sure what you mean.

~~~
jdpigeon
_commas - > parentheses_

------
genezeta
I was tempted to cite Antoine de Saint-Exupéry's quote on perfection, but we
all know that, right?

But then again, even the author mentions at the beginning the "risk of adding
too much" and, well, I wonder if this has not already happened to JavaScript.

~~~
RHSeeger
To be fair, since JS needs to be so backwards compatible, it's difficult to
take things away.

------
danShumway
I feel like there's something I'm missing with do expressions. Are they purely
syntactic sugar? What can I do with a do expression that I can't do with an
immediately invoked anonymous function?

The examples the article gives don't really look all that syntactically
different to me.

    
    
      const func = (() => {
        let result; // cache
        return () => {
          if (result === undefined) {
            result = someComputation();
          }
          return result;
        }
      })();
    
      const func = do {
        let result;
        () => {
          if (result === undefined) {
            result = someComputation();
          }
          return result;
        };
      };
    

And I guess I wouldn't be able to recurse in a do expression like I can with a
named function? So it's not like I can actually universally stop using
immediately invoked expressions everywhere.

~~~
tristanstcyr
Syntactic sugar I not a bad thing. In any sufficiently complex language, most
features can be simulated with other primitives. If we didn't want that, we'd
all be writing lisp.

In this specific case, the IIF is harder to read. You have to go all the way
to the end of the expression in order to realize intent. With 'do', you see it
right away.

Like anything in engineering, it's a question of trade-offs. Do the benefits
of better readability outweigh the downside of making the language more
complex?

------
enobrev
A lot of great and well-thought-out suggestions here. Whenever taking deeper
dives into Javascript, I tend to find myself on 2ality.com though google
searches and whatnot. They do well when it comes to breaking down the language
and features.

I'm a fan of 2.2: destructuring switch, although I occasionally employ
something similar in JavaScript and other languages with a `switch` statement:

    
    
        const resource = await fetch(jsonService);
        switch (true) {
            case resource.status === 200:
                console.log(`size is ${resource.headers['Content-Length']}`);
                break;
    
            case resource.status === 404:
                console.log('JSON not found');
                break;
    
            case resource.status >= 400:
                throw new RequestError(res);
                break;
    
        }
    

It's missing the destructuring aspect, so the first case is a bit more
verbose, and I imagine there's better potential to optimize something like the
OP's example. But it has a similar effect to the example and works today.

Slightly related: I really wish `case` worked with brackets instead of break,
similar to OP's example. It looks cleaner and is easier to visually parse. I
suppose that's only better if you prefer brackets (which I do).

------
PretzelFisch
I would like a null conditional operator and null coalescing operator.

return data?.error?.message ?? "no error";

Also pattern matching would be a big win.

~~~
Forge36
Null coalescing is already possible with ||. Though I'd like ?? If it worked
for undefined coalescing as well.

~~~
coldtea
How does || differentiate null from e.g. 0 or ""?

~~~
GordonS
It doesn't - it tests for a "truthy" value.

------
alexandercrohde
What's really missing from javascript?

1\. The ability to run my code without transpiling and reinstall dependencies
for 10 minutes.

2\. The ability to know which of the last 5 version of javascript to use.

3\. Stability in my dependencies.

4\. Consolidation and standardizing on established frameworks.

Basically undoing all the mess the ecosystem has done over the last 8 years.

~~~
JeremyBanks
1\. Stop supporting IE11. 2\. Whatever has 99% support in browsers your users
actually use. 3\. Yes. 4\. It’s called React.

~~~
alexandercrohde
4\. I don't actively javascript anymore (for the reasons listed above), but
isn't there still a bunch of variation within that (e.g. something called
"Vue" and something called "redux")? Does google use this "react" ?

In most traditional languages if you learn a framework you'll get a decade of
use out if it. ROR was the first hacker community to really screw this up.

~~~
JeremyBanks
Redux is a nearly-ubiquitous complement to React, not an alternative. Redux
handles the data, React handles the interface.

Vue got a lot of attention on Hacker News because it was new, but it has
plateaued with at most 1/8th of React's market share. It is not going to be
replacing it. React is more dominant than any JavaScript technology has ever
been except maybe jQuery (whose responsibility was a lot narrower). It's not
my personal favourite, but I appreciate having a de-facto standard across
almost everything I have to work on.

Google is mostly irrelevant to this discussion: they have extreme Not Invented
Here syndrome and cling to terrible in-house technologies for front-end
development. (They have been gradually increasing their React adoption over
the last few years, but it will probably remain small.) They were once the
world leader in JavaScript development, but they haven't treated it seriously
and are now significantly worse than all of their competitors. If you go to
Google, you're committing to learning a lot of non-transferable knowledge (in
addition to a lot of valuable practices).

------
scarface74
I know it will never happen, but something like LINQ in C#. Not just functions
that iterate over collections, but the whole idea of language integrated
queries where code is turned into expression trees that can be parsed and
translated by providers.

For instance in C# you can take the same LINQ expressions and they can be
translated to sql by EF or MongoQuery by the Mongo driver at runtime.

~~~
kristiandupont
Without type safety, there doesn't seem to be much to gain from this besides
for a bit of syntax checking?

~~~
scarface74
In this case of LINQ, I can call methods like:

var retiredMales = custRepo.Find(c => c.Age > 65 and c.Sex == “M”)

and then have an interface with a member:

List<Customer> Find(Expression<Func<Customer,bool>> expression):

You can implement three classes for that interface - one that uses EF, one
that uses Mongo, and one that just uses in an memory List for unit testing and
they all translate the expression to the underlying data store.

Theoretically, you could have an “expression” type in JS that can be parsed by
the provider.

~~~
jauco
What you’re looking for is the backtick in lisp. Or more generally: lisp
macro’s where a function can inspect the ast of an argument and base it’s
result on that.

For those who gave never used linq: this allows the above function to
translate the function to a sql, sparql or mongo query depending on the
currently selected backend

------
bcherny
As an aside, I’m not seeing any novel features in this list. All of them seem
to be borrowed from other languages (Swift, F#, Haskell, ...).

With enough of this, at what point will all languages start to look identical
(Youngme Moon calls this “homogeneous heterogeneity”)?

And how often does a language come along that introduces something truly new,
rather than porting an idea from another popular language?

~~~
kristiandupont
Why should every city have a theatre?

Is there any reason to "leave features to others" if they make sense in your
own language? The only issue I can come up with is that some language might
die off because it doesn't have any unique features. But if that happens and
the momentum is not enough to keep it alive, that doesn't seem like loss to me
anyway.

~~~
bcherny
I don’t know, it just feels like a bit of a boring world if every language
looks the same, instead of prodding you to look at the world from a slightly
different angle.

~~~
baddox
I really don’t see this criticism. Yes, all of these features have existed in
some form in prior languages. But I wouldn’t say that this makes JS “look
like” Haskell.

~~~
bcherny
Agreed. But if F#’s pipe operator makes it to Hack and JavaScript, null
chaining makes it to C#, CoffeeScript, and JavaScript, value types make it to
Haskell, Clojure, and JS, etc.. Eventually the overlap between different
languages’ features grows larger.

~~~
mbo
Like, I absolutely agree with you here. Languages are starting to stabilise
when it comes to syntax, features are getting cross pollinated from other
languages faster, etc.

I think it's wonderful. Developers can move between languages interchangeably,
teaching burden is reduced, languages aren't dismissed because they lack
feature X.

~~~
bcherny
That’s a valid way to look at it, too.

------
c-smile
Here are things that I've added in Sciter's script
([https://sciter.com](https://sciter.com)):

'like' operator that is used as a match

    
    
        if( obj like /[A-Z]/g ) … // "string".match
        if( obj like {name:String} ) … // obj has name string field
        if( obj like [String,Object] ) … // obj is an array of at least two elements, string and object.
    

extended switch() :

    
    
        switch(obj) {
          case 1: … // standard JS    
          like /[A-Z]/g: … // RegExp, Object, Array match
          instanceof cls: … // obj instanceof cls 
          in [1,2,3]: … // obj  
        }

~~~
c-smile
Object literals to allow `-` in identifiers, so these are valid constructs:

    
    
        var style = {
          font-size: 10px, 
          border-width: 1em
        }  
    

New built-in value types: Length, Duration, Angle, Color. So these are valid
constructs:

    
    
       this.timer(200ms, function() {...});
       
       element.style.width = 1em;
        
       var c = rgb(128,128,0);
       assert c.r == 128; 
       var (h,s,v) = c.toHSV();
    

Float and Integer are distinct types as these are different entities indeed.

------
seniorsassycat
1.1 Comparing objects by value I _think_ I'd prefer a standard `equals`
method, or more likely a `[Symbol.equals]` method. With `[Symbol.equals]` and
`[Symbol.hash]` we could also build collections for objects.

------
igotsideas
After messing with Elixir for a while, i really enjoyed the piping feature.
I’m sure it will be added to js and I’m genuinely excited cause it makes
reading code easier for me, given that your function names tell you what the
function does

------
h1d
I wish more languages follow the sense of being fun to code like ruby.

[1, 2, 3] - [1, 3] #Yields [2]

The code is readable, intuitive and short yet many languages never have this
simplicity.

JS example. [https://stackoverflow.com/questions/1187518/how-to-get-
the-d...](https://stackoverflow.com/questions/1187518/how-to-get-the-
difference-between-two-arrays-in-javascript)

~~~
rgovostes
That doesn't seem super intuitive to me. I assume that

    
    
        [1, 2, 3] + [1] == [1, 2, 3, 1]
    

but then if I subtract [1] again, does it take away the first 1, or the last
1?

It turns out it removes both of them. So subtracting a list is not the inverse
of adding a list. In Python, there is the distinction between lists and sets,
and while both support the + operator, only sets support the - operator for
this reason.

~~~
ricardobeat
First one is called 'concatenation', second 'array difference'. It does
require a one-time look at documentation, which doesn't take anything away
from the fun or practical aspects. There is no expectation that they are
inverse or commutative.

~~~
chx
> There is no expectation that they are inverse or commutative

Please. I am not a Ruby coder but I fully expect any language that a + b - b
is just a and also a + b - a is just b.

~~~
lifthrasiir
Uh, floating point numbers don't.

------
_yawn
What we really need isn't more syntax, it's core features like threads,
operator overloading, a large std library, some real form of serialization
(JSON doesn't come close to counting), etc.

~~~
larzang
Or even more basic things like "a date/time implementation that isn't a horror
show" or "\w that behaves like \w in every single other regex implementation
out there when the unicode flag is on".

------
jillesvangurp
IMHO the list of changes presented here sort of half way gets you there to the
(apparently) platonic ideal of being more swift or Kotlin like. It's better
but seems a bit lacking in ambition because of backwards compatibility.

I'd argue backwards compatibility is actually a lot less important in the JS
world than it currently appears to be. Why not aggressively deprecate all the
uglyness, broken semantics, etc. If you write ES 2020, or whatever they will
call it, why would you still want broken floating point math, wacky equals
behavior that nobody can explain with a straight face, etc. Just deprecate it,
start disallowing it, and eventually remove it from the language in a couple
of versions.

Browser javascript is effectively a compilation target these days. Yes you can
write in it directly but many people don't do that. Transpilers seem to be the
norm these days. Mostly transpilers already do work around javascript legacy
but they still have to deal with hiding some of the ugliness.

The good news is that people update their browsers. So there is very little
need for transpilers to be able to target old versions of browsers. Mostly
they just need to deal with current and recent versions.

With the emergence of WASM, that is becoming a more obvious compilation
target. You can already run C, Kotlin, Rust, Code, etc. using it. Doing so,
you bypass all of the legacy of javascript. It's just another compilation
target for compilers.

It's not such a big leap to simply run javascript interpreters/vms on top of
WASM as well. Why not? It's not any different from doing the same with ruby or
python. It would be a great way to support legacy applications and it
completely removes the need for javascript to continue to support legacy
features. IMHO, it would greatly simplify browser implementations if all they
had to do is support running WASM + whatever standardized bindings they ship
for things like the DOM, browser storage, webgl, etc.

You could freeze a current version of v8, call it legacy js and use that to
run legacy javascript if/when you actually need it and rely on a modern JS
interpreter without any of the baggage. My guess is that running legacy js
would very rapidly become a niche thing.

------
Waterluvian
A built-in way to conveniently iterate n times.

Anyone have a sensible pure JavaScript example for:

    
    
        range(5).forEach(fn)
           

I haven't found one that felt small enough to use inline. It end up using
traditional for loops.

~~~
klardotsh
I really don't love it, but

    
    
        Array(5).fill().forEach(() => console.log('hello'))

~~~
ljm
I've also taken to:

    
    
        Array.from({ length: 5 }).forEach(...)
    

But I'm not sure what you gain from that compared to just wrapping a for loop
in a function. You still don't get ranges the way you do in, say Ruby:

    
    
        ('a'..'z').each {|letter| puts letter }

~~~
klardotsh
Yeah, I mostly work in Python, where ranging and slicing is amazing. JS has a
lot to catch up on in that regard.

------
int_19h
For value equality, I feel like the whole value/reference type distinction is
an unnecessary distraction. What we really need is different _operators_ to
handle object identity comparisons and value comparisons. Whether an object
can be meaningfully compared by value to another object is orthogonal to
whether it's a value type or not - e.g. arrays are reference types, but
there's no reason why any two arrays can't be compared element-by-element.

One language that gets it mostly right is Python, where "==" always compares
values, and "is" always compares references. Thus, you can use either for e.g.
lists and sets, depending on what you actually need at any given time.
However, Python fails in that it doesn't give the same flexibility in many
contexts where the comparisons are implicit - e.g. in dicts and sets (it
always uses ==, instead of letting the user specify an equality function).

Ironically, a language that gets it fully right - including the standard
library - is VB.NET.

I'm not even sure that value types themselves are such a useful concept. A
value type can be thought of as an immutable reference type, where any
mutations done via the reference simply cause a new object to be created and
the reference changed (semantically; under the hood, it can be easily
optimized down to in-place update).

------
pmontra
In this code please remove the when keyword if possible. It's only noise. The
Elixir equivalent works well without it. Other than that I'd love that kind of
pattern matching on JavaScript.

    
    
        case (resource) {
        when {status: 200, headers: {'Content-Length': s}} -> {
         console.log(`size is ${s}`);
        }
        when {status: 404} -> {
         console.log('JSON not found'); 
        }

~~~
moogly
They're going to have to iterate on that proposal IMO, until they've reached
something closer to Rust. C# 8 went with this[1], and that should be proof in
the pudding that you can make something more ergonomical/palatable in a
C-based language.

[1]: [https://blogs.msdn.microsoft.com/dotnet/2019/01/24/do-
more-w...](https://blogs.msdn.microsoft.com/dotnet/2019/01/24/do-more-with-
patterns-in-c-8-0/)

------
nudpiedo
why no one proposes as feature a header to make javascript strict with the
newest standard and deprecate and drop compatibility from previous versions in
the newer ones?

Or even just complete support for asmjs with interop and program in completely
other languages, especially since the person who wrote this seems to want to
program in languages with different semantics...

~~~
_ZeD_
Isn't enough the "strict mode"?

[https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Strict_mode)

~~~
nudpiedo
honestly, I would see dropping features and select a leaner ES6+ version
within the html tag as a very good "feature". The strict mode also does not
fix some historical inconsistencies and design bugs just preserved for
compatibility, for example methods on default arrays vs methods on the
querySelector results.

------
_bxg1
Much of this reads like the author wants JavaScript to be something it isn't.
The requests for compare-by-value data structures, immutability, and true
concurrency are huge paradigm shifts from the heart of the language whose
implementation would no doubt come with significant complexity overhead.

~~~
_bxg1
It'd be like writing a post saying "all that C++ still needs are fully-dynamic
data structures and garbage collection"

Well, uh, okay

~~~
c-smile
That will be D language then: [https://dlang.org/](https://dlang.org/)

~~~
_bxg1
Exactly- a new language, not a retrofit on an existing one

------
benbristow
I'd like to see guard clauses. Don't know how they'd be implemented though.

------
reconbot
I love these proposals and while it probably makes sense for me to learn more
about the languages that already implement it there's no denying that having
them in JS is a win. =)

I wish I knew about python's
[itertools]([https://docs.python.org/3/library/itertools.html](https://docs.python.org/3/library/itertools.html))
before I started making [streaming-
iterables]([https://www.npmjs.com/package/streaming-
iterables#api](https://www.npmjs.com/package/streaming-iterables#api)) for
example.

------
TravelTechGuy
Not sure if it had been mentioned already, but I think we could benefit from
cancellable Promises.

Many of the modules and libraries currently support Promises, and
`async/await`, but try to find a way to cancel a long-running Promise. Some
libraries have resorted to using their own mechanisms, and some (e.g. `axios`)
resorted to using a withdrawn TC39 proposal.

I'd like a standard, easy way to cancel a Promise. I know it's not simple, and
Promises can wrap around other Promises, etc. but if I'm e.g. uploading a
large file to a server, there should be an easy way to provide my users with a
"cancel" button that works.

~~~
tjallingt
theres the AbortController [1] that can be used on a few new promise based
APIs such as the Fetch API [2]

[1] [https://developer.mozilla.org/en-
US/docs/Web/API/AbortContro...](https://developer.mozilla.org/en-
US/docs/Web/API/AbortController)

[2] [https://developer.mozilla.org/en-
US/docs/Web/API/Fetch_API](https://developer.mozilla.org/en-
US/docs/Web/API/Fetch_API)

~~~
TravelTechGuy
But there's no easy way to inject this AbortControl into existing libraries,
or use it easily with async/await. I'm looking for an easier standard.

------
amelius
Simplicity and elegance

~~~
Paperweight
I feel like you could take a subset of JavaScript and make a new "JSLite"
language that forces you to do things in one, proper best way. It would be a
great way for new developers to learn modern techniques.

~~~
tapirl
Reading is more frequently than writing in reality.

------
luord
Entirely off-topic (or maybe not given the nature of like 20% of the comments
here), but it's getting seriously annoying how in _every_ thread even
tangentially related to programming there are ten or so people (not the same
people, not trying to call anyone out) trying to proselytize about functional
programming and/or static typing.

They're _not_ panaceas, they're just alternative approaches to programming no
worse nor _better_ and they don't need to be brought up in every. Single.
Post.

~~~
nambit
How can you say they're no worse nor better? That's a loaded statement without
qualification. It is entirely possible (if not highly likely) that one
approach _is_ better (for all or most use cases)

~~~
luord
> That's a loaded statement without qualification.

That "without qualification" is an odd thing to say regarding a couple of
(flamewars) discussions that have been going on for _sixty years_. There are
thousands, millions of lines of literature discussing advantages and
disadvantages of _every_ paradigm and _every_ type system.

> It is entirely possible (if not highly likely) that one approach is better

And when such approach is discovered and/or conclusively proven as better, I'm
sure the entire industry will converge upon it. Computer science is, after
all, a _science_.

What I just wrote is obvious and common knowledge that I'm sure you're aware
of, but I couldn't think of another way to explain my point. Hopefully I was
clear.

------
austincheney
JavaScript still is often extended via inheritance, such as classes and
modules. This is unfortunate because inheritance is the opposite of simple,
putting things together instead of taking them apart.

One addition that could greatly improve simplicity is assignable blocks. A
block provides scope, much like a function, but accepts no input and returns
nothing. Most functions I write take no input and return undefined because I
only need them for code reuse by reference.

Imagine code like this:

    
    
        if (expression) myBlock else otherBlock;

~~~
int_19h
What's the benefit over myBlock() and otherBlock()

~~~
austincheney
You can write code withou the curly braces, but is generally considered a bad
practice.

    
    
        if (expression) myBlock() else otherBlock()
    

is really

    
    
        if (expression) {myBlock();} else {otherBlock();}
    

In that case a block is still there and not reused. Both the semantics and
structure are different if the block itself were reused. You could extend the
above like so:

    
    
        if (expression) {myBlock();x+=1;} else {otherBlock();}
    

If instead the block were reused that would not work. A finer degree of
separation is imposed by reference that reenforces separated structures by
name. Separation is the foundation of simplicity. It also makes for code that
is easier to read like a natural language sentence.

~~~
int_19h
I'm still not following. Why would you want to reuse blocks (scopes?) rather
than code?

------
sebringj
Types :) I tend to use underscore for just _.get or _.set these days as I have
most everything else I want so having some built in way to do that would be
nice but the elephant in the room here is the toolchains being built quietly
behind the scenes that support web assembly from strongly typed languages with
much better load times and performance. The WALT one and the oCaml React one
is pretty cool but would be nice if there was an official strong-typed
JavaScript version for this scenario being built as a spec.

~~~
bluepnume
Ironically though, using _.get or _.set makes that code virtually impossible
to type.

~~~
sebringj
right because i wasnt sure of the type

------
devwastaken
Renderer access. It should be possible for js to control and get callbacks for
rendering and event updates in styling. Though that's a browser feature, not
really js

~~~
AgentME
You might be interested in the CSS Paint and Layout Worklet proposals.

------
pfooti
I'd like to see a real, useful implementation of weak references, for use in
value caches, or a way to manually take over garbage collection for some
objects.

Basically if I want to keep a cache of expensive objects that will grow
monotonically, I need to do a lot of work to make sure I'm not leaking memory
right now, but if I could instead store in a Map<string, weakref>, it'd be
much easier, things could go out of ref and I could deal with that.

~~~
ralusek
You're aware of WeakMap and WeakSet, right? You can't key by strings, but I'm
not sure you'd want to.

~~~
pfooti
Yeah weakmap and weakset basically do the opposite of what I want. The keys
are weakly held rather than the values, which does no good for an ORM cache.
One cannot even iterate over the keyset in a weakmap (so you can't do a lousy
o(n) lookup version of what I want.

------
mschuetz
The single most important feature I'd like to see in JS are types. Typescript
seems nice but I don't want to need a transpiler, especially since I'm doing a
lot of live-coding in JS and compilation/transpilation times >50ms are already
too much.

Other than that, JS could benefit tremendously from a more complete standard
library, and a build system, including a file watcher, that doesn't rely on
200 nonsensical dependencies.

~~~
james-mcelwain
I'm confused -- any static typing system is going to need a compilation step
to check types. How is typescript any different than "javascript with types"
would be? If your goal is live coding, a dynamically typed language will
always be faster.

~~~
mschuetz
Typescript right now requires basically two compilation steps, and it doesn't
have decades of compilation optimizations behind it that JS has. If types or
typescript itself, were directly supported, things would be so much faster.

------
jcoffland
Reminds me of this quote:

“Perfection is achieved when there is nothing left to take away.” --Antoine de
Saint-Exupéry

This principal should be applied more to programming languages.

------
louthy
Type safety

~~~
tobyhinloopen
Typescript!

~~~
louthy
Typescript is missing from JavaScript?

------
hsavit1
If Apple is indeed granted the patent on Swift's optional chaining (as was
recently posted [https://forums.swift.org/t/apple-is-indeed-patenting-
swift-f...](https://forums.swift.org/t/apple-is-indeed-patenting-swift-
features/19779) ), we wouldn't see this in future Javascript, would we?

~~~
k__
Is it even a useful feature?

Will it lead to many JS bugs simply disappearing and applications getting more
stable or will it just obfuscate bugs in the long run?

~~~
realharo
Presumably people will only use it in situations where null (or undefined, or
however it works) is a valid and expected value for a particular property.

In TypeScript, these properties would explicitly have a `MyType|null` or
`MyType|undefined` type.

------
superasn
I wish JavaScript would let me declare scoped variables inside if statements
like this:

    
    
        if (let myVar = exp) {

~~~
dan-robertson
Should this also behave like if-let in rust so if the pattern doesn’t match
then it acts as false? E.g

    
    
      if(let [x,y] = thing) ...
    

Would only be true when thing is an array with two elements (not super sure
bow pattern matching is defined in ha. I would guess that actually if the
match fails then things get to be undefined which means the match can’t really
fail so this would likely be confusing)

------
defnotarobot
As someome who has spent the last year primarily with Elixir, it is impossible
to overstate how nice pipeline |> is.

It is one of the most convenient and readable pieces of syntax, and one of the
primary reasons I pause before using javascript instead of Elixir in a given
moment.

------
jkbbwr
The biggest thing that is missing in my experience is finalizers or context
blocks. I need some way to free resources allocated inside emscripten. Either
python style context managers or go style defer or c++ style destructors on
scopes would be a god send.

------
zzo38computer
Some things I wanted in JavaScript is a goto command, and macros. They mention
objects to compare by value, and I had a bit different idea which is a
UserPrimitive function which has a similar meaning (but different; user
primitives would also allow some degree (but still restricted in some ways) of
operator overloading).

Bigint is a good thing they added. Sometimes you need to deal with 64-bit
integers, and other times you might want unlimited range, so that helps.
However, some operations that are not currently built-in perhaps should be in
order to be more efficient, such as log2 of bigint numbers or counting
trailing bits.

A built-in RegExp.quote() function is another thing (although it is easy to
make, it seem to me it is something that ought to be built-in).

If macros are implemented, then many features can be done by macros and do not
need to be built-in features.

One feature of JavaScript that I dislike is automatic semicolons.

And yet, many proposals (including these) may be rejected if better ways are
found.

------
blitmap
The only thing I absolutely hate is the import-as bullshit inspired by Python.
I was told this was to assist static analyzers, but when you compare it to
destructuring syntax you just get annoyed by the duplication.

+1 for pipeline operator +1 for a destructuring switch

------
vertline3
I like JS, I like the new features since es5, although I liked es5 also, I
notice that the updates are now yearly it seems, which is mostly good since
there was maybe a long delay in the past, but can be bad if there become too
many features.

------
stevedekorte
Coroutines (unlike async/await) would allow one to wait on an async result
without losing the stack which is waiting. This would avoid the need for
(often) inscrutable callback state machines (Javascript's version of GOTOS).

~~~
int_19h
Async/await has a logical stack that is distinct from the physical stack(s).
Quality tooling would show you said logical stack instead of a mish-mash of
physical stacks from different async functions that happen to be intermingled
on the current thread. Example:

[https://blogs.msdn.microsoft.com/devops/2013/07/01/debugging...](https://blogs.msdn.microsoft.com/devops/2013/07/01/debugging-
asynchronous-code-in-visual-studio-2013-call-stack-enhancements/)

So you don't need to deal with "inscrutable callback state machines" anymore
so than you need to deal with the assembly labels and jumps that your for-loop
compiles into.

------
paulie_a
Not having to install the shit show of conflicting dependencies. Get a formal
package manager instead of the twenty different ones. Basically take a look at
python and realize the js ecosystem could actually have nice things.

------
renke1
Save navigation and equals/hashCode (as protocol, not using that ValueType
thingy) are fine and definitely useful, but otherwise we should avoid adding
even more syntax elements to JS.

------
servercobra
I would love some of the syntactic sugar from Swift. Swift's `guard` and `if
let`, along with the nil coalescing mentioned in another comment make Swift a
joy to write.

------
jorblumesea
I know this is a really contentious idea but I'd like javascript and
typescript to merge into the same language. Type safety is the huge 800lb
gorrilla in the room.

~~~
moogly
I know at least I will never ever go back to JavaScript after having used
TypeScript for a bunch of years.

------
pcmaffey
> Comparing objects by value

A handy helper I use all the time for this:

    
    
      export const jsonEqual = (a, b) =>
      JSON.stringify(inspect(a)) === JSON.stringify(inspect(b))

------
adrianhel
I would like optional typing for the few places where it would be very useful.
But people would enforce all types way too much, so I'm kind of on the fence.

------
z3t4
Please stop adding new syntax, it only makes the language uglier. Instead of a
special syntax for comparing objects, just add a function to Object.prototype.

~~~
zzo38computer
I disagree. Stuff should not be added to Object.prototype, although it can be
a function in Object itself (such as Object.equals or something, not
Object.prototype.equals). You are correct it does not need a new syntax
though.

------
Findeton
I'm implementing delta JSON mutations. I think with time people will embrace
the idea.

------
8bitsrule
Python writes to _local_ files using native function _open_. Javascript has no
native function. Because...?

We can open a window, write arbitrary text to it, use the browswer to 'Save
as...'. Close but no cigar.

I'd like to use JS for a lot more than the web. MS Basic wrote user-entered
text to a file 40 years ago.

~~~
thomasfoster96
I’m not sure about you, but I consider JavaScript running in my browser not
being able to write arbitrary text to files to be a good thing.

Otherwise, Node.js can do exactly what you just said Python and MS Basic can
do.

~~~
8bitsrule
Whoa hoss. Bit of overkill there.

I can already write to file with 'window.(something).createObjectURL' ...
which queries user. I simply suggest that 'future javascript' _open()_ (or
whatever) supports that with no-more-code needed.

------
stephenr
Really? Still no way to specify argument types?

~~~
c-smile
Just argument types are useless.

Either all variables shall have types and so static, compile time analysis can
be performed.

Or no types at all.

~~~
stephenr
Useless to you maybe.

~~~
c-smile
To anyone.

Types are useful only if they can be checked/verified at compile time.

Types in only arguments cannot be verified by compiler. So type mismatch can
be checked only at runtime. So would be this:

    
    
       function foo(a:Number) { … }
    

Is essentially this:

    
    
       function foo(a) {
         if( !(a instanceof Number)) throw "Not a number";
         … 
       }
    

And that rises the question: who and at what moment will handle such errors?

~~~
int_19h
Types are a subset of contracts. Contracts are plenty useful at runtime, as
evidenced by the fact that every language that offers design-by-contract (most
of which are statically typed) still provides runtime checking facilities.
Furthermore, they're usually enabled by default for preconditions, which is
what argument types are.

Nobody will handle such errors - contract violations are not supposed to be
handled, they're a coding bug. You "handle" them by fixing the caller such
that it never violates the contract. The benefit of having a runtime check is
that it can detect the error instead of silent wrong behavior potentially
followed by state corruption and further bugs. Most of the time, these will
fire in automated tests, anyway.

------
IloveHN84
Type safety, without using Typescript

------
rpz
>1 2 3 + 3 4 5

4 6 8

Extend the operators to work on lists!

~~~
nrb
That would confuse the heck out of people who would expect + to concat the
arrays (I guess not much better than the current behavior of string concat)

Obligatory lodash reference: _.map(_.unzip(arrays), _.sum)

------
topynate
Dependent types. You think I'm joking, but check back in ten years...

------
pragueexpat
Deep clone objects

------
madmaniak
ES5 > ES6+

------
amiga-workbench
Developers with pragmatism and taste.

------
AnyTimeTraveler
A sane and coherent dependency structure?

------
tomerbd
static typing

------
andrewmcwatters
Restraint.

------
jenscow
A viable replacement.

~~~
sctb
There will probably always be a snarky, jejune dismissal in every JavaScript
thread, but commenters should think to themselves, “It doesn't have to be me.”

~~~
yakshaving_jgt
I had that thought, but I don’t think it’s that unreasonable to hold
JavaScript as a lost cause. Enough large projects are just using JavaScript as
a sort of bytecode for the web anyway.

I also find personally that recognising something as bad is the first step to
making something better.

~~~
sctb
That's fine, but we're here to gratify our intellectual curiosity. There's
nothing wrong with recognizing as such, it just needs to be done thoughtfully
and informatively so we can have a discussion about it.

------
amelius
A reference implementation in a purely functional language.

~~~
snek
How about a reference implementation in itself :P
[https://github.com/devsnek/engine262](https://github.com/devsnek/engine262)

------
JustSomeNobody
Needs more punctuation. I don’t know about the rest of y’all but I get
disappointed when I look at JavaScript and it’s not awash in a sea of angle
brackets.

But seriously, be careful not to ruin it with too much “I need this!”

------
alan_n
I was scrolling through this and I was like, where is optional chaining? Then
when he gets to it, he's a bit against it because it's easy to write bad code.
What? People still do nested checks like this all the time! It's not changing
anything beyond making them more readable and I think more obvious when you
think something might not exist (e.g. `some.very?.nested?.property` vs
`some.very.nested?.property`). Sometimes it's not so much the nesting that
makes it unreadable either, but that you're checking multiple objects, imagine
writing `some.nested?.property && other.nested?.property`.

It's like saying promises shouldn't have been added because you can still
write bad code with them and create nested promise hells ( I sure did when I
first learned them). Isn't it better to say, no, best practice is to add ?
only where you actually need them. The solutions he cites are not really
solutions, they're just abstracting the problem away which most of as already
do, but then the abstraction becomes hard to read because those checks have to
be somewhere.

Worse I think is the comparing objects by value idea. I think it would be way
too easy for beginners to reach for that solution. There are often better work
arounds, it's quite rare imo to need to do a deep nested check of two objects
that could be very different (i.e. contain different properties). Plus it's
not like using a helper function is that verbose and a custom one can be
really fast (if both objects contain the same properties, creating a function
that "manually" checks for them all is blazing fast), same thing with deep
clones.

Things I would really like, from most to least: optional chaining obviously,
realms, pipeline operator, optional type checking, and enums (you can sort of
fake them though, hence why they're last)

~~~
regularfry
The difference between Promise and optional chaining is that you can (and
should) redesign your code not to need deeply nested data access. The pain of
doing deep null checks everywhere is a code smell in itself, and optional
chaining just covers it up. Making a bad thing easier to do is bad.

You can't redesign your code not to need asynchronous callbacks. They're
fundamental, given the constraints of the language implementation. Given that,
it makes sense to make them as painless as possible (and I'd argue async/await
is... well, it's not good, but it's better than Promise alone).

~~~
alan_n
Right, but sometimes that's just not possible, what then? The only option is
to abstract the check into a function which just moves the problem. Or to use
a third party library to do exactly what optional chaining does. When I see
this argument I just think why should I have to suffer because other people
don't know best practices and will produce bad code regardless of whether this
exists or not?

I would argue it makes a bad thing easier to read so you can actually tell
what's being checked. It's already an easy thing to do, leaving things as they
are is not deterring anyone, except it reads horribly. I'm not arguing too
many aren't code smell, but what about when it's needed?

The object check syntax proposed on the other hand, imo, does not make things
clearer and is of little benefit.

Also regarding callbacks, I meant you can avoid callback hell without using
promises. Promises were not technically needed to solve that problem even
though they're talked about as a solution to it, you can have promise hells
too (and they are exactly what most beginners do with promises). But if used
right, the solution is much easier to read and understand. That's how I see
optional chaining.

