Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is JavaScript a type-safe language?

I have read that JavaScript is not a type-safe language, but I am not sure how true is that.

Say I have the following code:

<script>
    var i = 123;  // i is an int
    i();  // treat i as a function (this will produce an error)
</script>

When I run this code I get the following error:

enter image description here

So basically I was not allowed to treat an int variable as a function, doesn't this means that JavaScript is a type-safe language?

like image 548
user4582812 Avatar asked Sep 22 '16 15:09

user4582812


3 Answers

Type safety is a complex topic and there's no one agreed definition of what exactly a "type-safe" language is. But by almost any definition of it, no, JavaScript is not type-safe. :-) In that particular example, though, JavaScript did provide runtime type safety: It didn't actually try to call i and cause some kind of memory access exception or similar; instead, when your code tried to call it, the first thing the JavaScript engine did was check to see if it was callable and, since it isn't, it raised a protective error.

But a type-safe language tries to discourage or prevent errors or undesireable behavior due to using an incorrect type, through type enforcement (both at the compilation/parsing stage and when the code runs). JavaScript mostly doesn't do that (the above notwithstanding); in general, JavaScript tends to coerce instead.

For instance, in a type-safe language, this would probably fail:

console.log("hi there" * 4);

...assuming * isn't a defined operator for strings. (I believe there's at least one language where it is and that would result in "hi therehi therehi therehi there").

But in JavaScript, * doesn't have a defined meaning for strings. But rather than causing an error (at the compilation/parsing stage or when run), the string is implicitly converted to a number n, and then used in the expression n * 4. In the case of the string "hi there", the coercion results in the value NaN ("not a number") rather than causing an error (and then NaN * 4 also results in NaN).

Type-safe languages also typically (though I don't think always) have typed variables/parameters/properties and similar and do at least some type checking at compilation/parsing stage rather than when the relevant code runs. In those languages, i would have had a type associated with it (e.g., int i rather than var i), and the code trying to call it as a function would have failed at the compilation/parsing stage, rather than later when it was run as it does in JavaScript. JavaScript, on the other hand, doesn't have typed variables/parameters/properties at all. A variable can hold an object one moment and a primitive number the next.

One of the benefits of that is that JavaScript is friendly to duck-typing (if it looks like a duck and quacks like a duck, it's a duck). For instance, suppose you have a function that, notionally, requires a string:

function capitalize(str) {
    return str.charAt(0).toUpperCase() + str.substring(1);
}

In JavaScript, the following code calling it:

capitalize(42);

is perfectly correct and will not raise any error when the code containing that call is compiled/parsed. But it will raise an error when the code is called — not because 42 isn't a string (it isn't, but that's not the point), but because 42 doesn't have a charAt method.

In language with static type safety (e.g., compilation/parsing stage type safety), there'd be type information associated with the str argument and the error would be when the code was compiled/parsed.

But in JavaScript, not only is it happy to compile/parse that code, but it's happy to run it on a non-string provided whatever you give it meets these criteria:

  1. It has a charAt method that returns something with a toUpperCase method, and

  2. It has a substring method.

As long as you give it something meeting those criteria, whether that thing is a string or not, it'll work.

function capitalize(str) {
    return str.charAt(0).toUpperCase() + str.substring(1);
}

var thingy = {
    charAt: function() {
        return {
          toUpperCase: function() {
            return 40;
          }
        }
    },
    substring: function() {
        return 2;
    }
};

console.log(capitalize(thingy)); // 42

;-)

like image 175
T.J. Crowder Avatar answered Oct 13 '22 11:10

T.J. Crowder


Javascript (alongside Java, Ruby, Haskell) is a type safe language (whereas C is not).

On the other hand, JavaScript does not guarantee static (compile-time) type safety which is something different (described by @t-j-crowder answer).


In simple words it says that a language is type safe if it always has a useful answer for any piece of code.

The free book Why Rust states the following (on page 4):

A type-safe language [...] assigns a meaning to every operation, even if that meaning is just to raise an exception.

And the type error you got is precisely the meaning of your operation.

In contrast C ist not type safe (although it checks code at compile time) because you can create cases where it will not throw an exception, but just do something random. (see pages 3 and 4 of the Why Rust book for an example) The implicit type coercions which are present in Javascript (e.g. casting the number 1 to a string 1 for string concatenation: 1 + '2') is a well defined operation.

The Why Rust book defines well defined in the following way:

If a program has been written so that no possible execution can exhibit undefined behavior, we say that program is well defined.

Using the definition of well defined it continues:

If a language’s type system ensures that every program is well defined, we say that language is type safe.

This note about type safety is also interesting:

Note that being type safe is mostly independent of whether a language checks types at compile time or at run time: C checks at compile time, and is not type safe; Python checks at runtime, and is type safe. Any practical type-safe language must do at least some checks (array bounds checks, for example) at runtime.


It's a bit ironical that a book about Rust helped me to understand Javascript better!

like image 33
Andru Avatar answered Oct 13 '22 10:10

Andru


That type error happens at run time though, not compile time.

Obviously it will fail at some point if you try to treat a number as a function.

In a safe language, it would catch that during compilation so the bad code could never be run in the first place to fail during runtime.

like image 27
Carcigenicate Avatar answered Oct 13 '22 12:10

Carcigenicate