I think it's dishonest and counterproductive to pretend that all languages are equal. TypeScript is in fact born out of the problematic reality that writing JavaScript on large teams is more expensive than it needs to be.
JavaScript is very confusing, to the extent that "confusing" is the opposite of "easy to predict" and "understanding it is not complex".
For a tiny selection of the confusing complexity of JavaScript:
{var x = 1; {var x = 2;} console.log(x)} is 2 but {let x = 1; {let x = 2;} console.log(x)} makes 1.
doThing(arg, (x) => { this.receive(x); }); will generally work when doThing(arg, function(x) { this.receive(x); }); will generally not.
[1, 2, 3, 10, 20, 30].sort() is [1, 10, 2, 20, 3, 30]. ["x", "y", "z", "a", "b", "c"].sort((a, b) => a - b) doesn't change the order.
"slice" in [] but for (let p in []) {console.log(p);} prints nothing.
"𝕋𝕋".length is 4 but Array.from("𝕋𝕋").length is 2
{[{}]: 10}["[object Object]"] is 10 even though that obviously doesn't make sense
(a => a).length is 1, and Array.from((a, b) => a + b) is [undefined, undefined].
arr.push, arr.pop, arr.splice don't return the array, but modify it; arr.sort and arr.reverse modify and return the array; arr.concat, arr.slice, arr.map don't modify the array.
0 == "0" and 0 == "" but "" != "0".
Certainly all of these have (relatively straightforward) explanations. However, each of these represents a piece of "trivia" that you have to know in order to actually understand JavaScript code in the wild; they are each some of the many hurdles that a programmer has to (repeatedly) overcome in order to not be confused by JavaScript. This is also, obviously, only a selection of a few prominent examples of JavaScript-generated confusion; knowing these does not mean you won't be confused by JavaScript.
Still, JavaScript is definitely not the most confusing language. And I agree, it is getting a lot better (especially as co-existing tools like the TypeScript compiler make writing plain-old-JavaScript easier). (Unfortunately, as the above list somewhat illustrates, this also makes JavaScript more complex as "legacy" features coexist alongside "modern" features with subtly different behaviors).
As someone who tries to keep up with programming language theory, I am afraid of complacency with programming tools. Where we are today is obviously nowhere near the "apex" of programming tools (after all, programming languages themselves are only about 60 years old at this point) which means there should be lots to improve upon. Recognizing we have come far is important, but it is also very important to not ignore areas that we can still improve.
(The above is not unique to JavaScript, though I do think JavaScript is one the more-confusion-inducing end of the spectrum. C is probably the contender for the language most are often called "good enough" despite well-identified areas were it deserves improvement).
Like @jordan
explained, Javascript is weird. But examples like let and var is an attempt to solve this not another weirdness. I had one of my most disturbing bugs recently, I completely forgot typeOf NaN === number. That is literally saying 'not a number` is a number.
But just like the English language understanding this weirdness is the only way you can call yourself a fluent speaker of the language. In English you could say 'Giver her her book', but never "Give him him book". "How are you" is supposed to be for a singular person, but "are" in every other situation is plural. We just accept this "bug" and appreciate the more expressive part of the language.
I understand the point you made regarding English language, but comparing shortcomings of English language with a programming language is like comparing human brain with AI.
To be fair, the first few examples don't line up specifically because the old language feature was not obvious and new languages features were added to correct this (e.g. variable scoping with let vs var). Also, the arrow function vs normal function are explicitly different because it's actually useful to have both. Sometimes you want to stay in the current scope, sometimes you want an isolated scope.
There are many things JS does weird, but several of these examples are attempts to correct things. With the variable example, it's very recommended to just not use var ever and just use const or let. But var must be kept around for compatibility.
That said, TS is absolutely trying to make things even better and helping remove a full class of errors that compiled language devs haven't had to think about for decades.
JavaScript is not confusing. It's confusing for people who didn't read specification, don't understand type coersions and how weak typing works, don't understand closures, etc. Then, of course, you can give an example of:
0=="0"0==""""!="0"
But actually there is nothing confusing here. JavaScript uses number coersion when comparing things of different types, but "" and "0" are both strings so JavaScript just compares them straightway.
It might be daunting to know those things, but it's certainly not confusing when you know them!
Furthermore, you shouldn't even use code like that in production in the first place.
Talking about this weirdness:
{[{}]:10}["[object Object]"]
It does make sense. JavaScript is not a human language, it's a programming languge with strict rules and syntax and, in my opinion, a developer should think in those rules. Here you create a new object defining ES6 computed property as an empty object. In JavaScript object keys can only be strings, so the method toString() is being called on {} which by default produces [object Object] string. So basically you are doing this:
Again, the fact that it's possible to write code like this doesn't mean you should do it. But it is great to understand how the language works! So it shouldn't be confusing for a developer.
Of course, that's just my opinion on that. I love JavaScript ❤️
UPD: I actually got inspired by this thread and wrote a little guide about JS type conversions:
As I mention, all of these behaviors have fairly concise explanations. However, having to know dozens of bizarre edge-cases that do come up in real code reduces the space you have in your head for thinking about the important problems in front of you (and instead you have to spend time thinking about language trivia).
These complexities are not inherent to implicit type coercion, dynamic typing, closures, etc. Lua has all of these things, and none of the gotchas that JavaScript has.
For example, in Lua, tables are simply mappings from objects to keys. == is the same thing as what determines if two keys map to the same value (so, e.g., {[{}] = true} actually does do what it says it does; 1 == "1" is false, t[1] isn't the same thing as t["1"]; but you can write 1 + "1" == 2 yet 1 .. "1" == "11").
The fact is, that many of JavaScript's complexities/gotchas/points of confusion are unnecessary. The fact that they can be overcome is secondary to the fact that you have to overcome them: if JavaScript were designed slightly differently, you would not have to worry about them (and this can in most cases be done without sacrificing any linguistic power and convenience)
The actually confusing part here is trying to understand, why did they even design a language this way, and why people still insist on using it.
Weak typing does not necessarily mandate type coersion. There's a lot of dynamically typed languages that make sense, that did not opt out for insane rules.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
I think it's dishonest and counterproductive to pretend that all languages are equal. TypeScript is in fact born out of the problematic reality that writing JavaScript on large teams is more expensive than it needs to be.
JavaScript is very confusing, to the extent that "confusing" is the opposite of "easy to predict" and "understanding it is not complex".
For a tiny selection of the confusing complexity of JavaScript:
{var x = 1; {var x = 2;} console.log(x)}
is2
but{let x = 1; {let x = 2;} console.log(x)}
makes1
.doThing(arg, (x) => { this.receive(x); });
will generally work whendoThing(arg, function(x) { this.receive(x); });
will generally not.[1, 2, 3, 10, 20, 30].sort()
is[1, 10, 2, 20, 3, 30]
.["x", "y", "z", "a", "b", "c"].sort((a, b) => a - b)
doesn't change the order."slice" in []
butfor (let p in []) {console.log(p);}
prints nothing."𝕋𝕋".length
is4
butArray.from("𝕋𝕋").length
is2
{[{}]: 10}["[object Object]"]
is10
even though that obviously doesn't make sense(a => a).length
is1
, andArray.from((a, b) => a + b)
is[undefined, undefined]
.arr.push
,arr.pop
,arr.splice
don't return the array, but modify it;arr.sort
andarr.reverse
modify and return the array;arr.concat
,arr.slice
,arr.map
don't modify the array.0 == "0"
and0 == ""
but"" != "0"
.Certainly all of these have (relatively straightforward) explanations. However, each of these represents a piece of "trivia" that you have to know in order to actually understand JavaScript code in the wild; they are each some of the many hurdles that a programmer has to (repeatedly) overcome in order to not be confused by JavaScript. This is also, obviously, only a selection of a few prominent examples of JavaScript-generated confusion; knowing these does not mean you won't be confused by JavaScript.
Still, JavaScript is definitely not the most confusing language. And I agree, it is getting a lot better (especially as co-existing tools like the TypeScript compiler make writing plain-old-JavaScript easier). (Unfortunately, as the above list somewhat illustrates, this also makes JavaScript more complex as "legacy" features coexist alongside "modern" features with subtly different behaviors).
As someone who tries to keep up with programming language theory, I am afraid of complacency with programming tools. Where we are today is obviously nowhere near the "apex" of programming tools (after all, programming languages themselves are only about 60 years old at this point) which means there should be lots to improve upon. Recognizing we have come far is important, but it is also very important to not ignore areas that we can still improve.
(The above is not unique to JavaScript, though I do think JavaScript is one the more-confusion-inducing end of the spectrum. C is probably the contender for the language most are often called "good enough" despite well-identified areas were it deserves improvement).
Like @jordan explained, Javascript is weird. But examples like
let
andvar
is an attempt to solve this not another weirdness. I had one of my most disturbing bugs recently, I completely forgottypeOf NaN === number
. That is literally saying 'not a number` is a number.But just like the English language understanding this weirdness is the only way you can call yourself a fluent speaker of the language. In English you could say 'Giver her her book', but never "Give him him book". "How are you" is supposed to be for a singular person, but "are" in every other situation is plural. We just accept this "bug" and appreciate the more expressive part of the language.
I understand the point you made regarding English language, but comparing shortcomings of English language with a programming language is like comparing human brain with AI.
To be fair, the first few examples don't line up specifically because the old language feature was not obvious and new languages features were added to correct this (e.g. variable scoping with
let
vsvar
). Also, the arrow function vs normal function are explicitly different because it's actually useful to have both. Sometimes you want to stay in the current scope, sometimes you want an isolated scope.There are many things JS does weird, but several of these examples are attempts to correct things. With the variable example, it's very recommended to just not use
var
ever and just useconst
orlet
. Butvar
must be kept around for compatibility.That said, TS is absolutely trying to make things even better and helping remove a full class of errors that compiled language devs haven't had to think about for decades.
JavaScript is not confusing. It's confusing for people who didn't read specification, don't understand type coersions and how weak typing works, don't understand closures, etc. Then, of course, you can give an example of:
But actually there is nothing confusing here. JavaScript uses number coersion when comparing things of different types, but "" and "0" are both strings so JavaScript just compares them straightway.
It might be daunting to know those things, but it's certainly not confusing when you know them!
Furthermore, you shouldn't even use code like that in production in the first place.
Talking about this weirdness:
It does make sense. JavaScript is not a human language, it's a programming languge with strict rules and syntax and, in my opinion, a developer should think in those rules. Here you create a new object defining ES6 computed property as an empty object. In JavaScript object keys can only be strings, so the method toString() is being called on {} which by default produces [object Object] string. So basically you are doing this:
Again, the fact that it's possible to write code like this doesn't mean you should do it. But it is great to understand how the language works! So it shouldn't be confusing for a developer.
Of course, that's just my opinion on that. I love JavaScript ❤️
UPD: I actually got inspired by this thread and wrote a little guide about JS type conversions:
Understanding JavaScript type conversions
Anton Melnyk ・ Aug 31 ・ 4 min read
As I mention, all of these behaviors have fairly concise explanations. However, having to know dozens of bizarre edge-cases that do come up in real code reduces the space you have in your head for thinking about the important problems in front of you (and instead you have to spend time thinking about language trivia).
These complexities are not inherent to implicit type coercion, dynamic typing, closures, etc. Lua has all of these things, and none of the gotchas that JavaScript has.
For example, in Lua, tables are simply mappings from objects to keys.
==
is the same thing as what determines if two keys map to the same value (so, e.g.,{[{}] = true}
actually does do what it says it does;1 == "1"
is false,t[1]
isn't the same thing ast["1"]
; but you can write1 + "1" == 2
yet1 .. "1" == "11"
).The fact is, that many of JavaScript's complexities/gotchas/points of confusion are unnecessary. The fact that they can be overcome is secondary to the fact that you have to overcome them: if JavaScript were designed slightly differently, you would not have to worry about them (and this can in most cases be done without sacrificing any linguistic power and convenience)
The actually confusing part here is trying to understand, why did they even design a language this way, and why people still insist on using it.
Weak typing does not necessarily mandate type coersion. There's a lot of dynamically typed languages that make sense, that did not opt out for insane rules.