4

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?

Andrew Grimm
  • 70,470
  • 47
  • 186
  • 310
user4582812
  • 531
  • 3
  • 14
  • type safe as in JS will auto-convert some things to appropriate DIFFERENT types for you, e.g. `alert('himom' + 42)` will output `himom42`, even though "string + integer" makes no mathematical sense. – Marc B Sep 22 '16 at 15:20
  • Well an integer isn't a function, what did you expect to happen if not an error indicating that fact? – Alex K. Sep 22 '16 at 15:20
  • 3
    Javascript has dynamic type safety, with other words it raises exception for some TypeError at run time. Generally` type-safe language` is a term used to indicate that this validation occurs before the code is executed. – Alex Sep 22 '16 at 15:23
  • Have a look at [this talk](https://www.destroyallsoftware.com/talks/wat) that demonstrates some of the really weird behaviour in JS which exploits the parsing rules and thus exposes the lack of type-safety. You can also read the [explanation of the behaviour](http://stackoverflow.com/questions/9032856/what-is-the-explanation-for-these-bizarre-javascript-behaviours-mentioned-in-the). There is also [this](http://stackoverflow.com/questions/38637003/what-is-happening-in-this-javascript-snippet) you can have a look at. – VLAZ Sep 22 '16 at 15:27

3 Answers3

30

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

;-)

T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639
  • 10
    The example at the end of this answer is a thing of beauty. – Jamiec Sep 22 '16 at 15:41
  • This is an example of duck-typing isn't it? The actual type doesn't matter; only that it meets the caller's criteria. – Carcigenicate Sep 22 '16 at 18:41
  • @Carcigenicate: My `capitalize` example at the end is, yes. But it's made possible by the lack of type safety, `capitalize` can't be declared such that it only accepts a string, not an object. (Type safety is a bit of a spectrum, though -- you can have duck-typing in a language that's type-safe if it has only one type for "object.") – T.J. Crowder Sep 23 '16 at 05:36
  • 1
    Aren't you mixing definitions of static/dynamic and strong/weak typing? Javascript and Python are both dynamic. Javascript isn't type safe (e.g. `'1' + 2`) but Python is. Python will only complain at runtime though. – Eric Duminil Sep 24 '17 at 10:53
  • @EricDuminil: Yes, I believe I was, thank you. I've updated to try to do a better job. Probably still not (remotely) perfect, but hopefully better. – T.J. Crowder Nov 16 '17 at 13:43
  • 1
    It looks great now! – Eric Duminil Nov 16 '17 at 13:48
  • 2
    As you mentioned yourself you're talking about *static type safety* here. I rather like a broader definition of *type safety* as I discussed in my reply. With that definition Javascript is indeed a type safe language. – Andru Mar 14 '19 at 10:31
  • "I believe there's at least one language where it is and that would result in "hi therehi therehi therehi there"" - Ruby comes to mind. Even though it doesn't have static typing, it is more likely to give runtime errors for mixing types inappropriately than JS does. – Andrew Grimm Dec 02 '19 at 22:57
7

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!

Andru
  • 3,953
  • 3
  • 29
  • 42
  • 2
    This is the definition of “type safety” which I’ve encountered previously: that no undefined behavior exists which could lead to misinterpretation of underlying data meant to represent one type as another. A scripting runtime having coercion rules is not misinterpretation; in fact, it’s an example of type safety in action. I see that wikipedia presents something very different (and very fuzzy) so I guess this definition may not be as commonplace as I’d thought? It looks a lot like people are mixing up type safety (which is pretty ubiquitous) with static typing though... – Semicolon Nov 29 '19 at 07:55
  • 2
    @Semicolon True. This common mixup of `type safety` and `static typing` (as in the reply of @t-j-crowder) confused me for quite a bit until I finally came across the rust book to read a definition which finally made complete sense. – Andru Nov 30 '19 at 11:12
1

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.

Carcigenicate
  • 35,934
  • 8
  • 48
  • 81
  • So an interpreted language can never be type-safe, only a compiled language can? – user4582812 Sep 22 '16 at 15:29
  • 1
    @user4582812: No, an interpreted language can be typesafe. It's just that the type-related error occurs during the parsing stage rather than waiting for the offending line of code to be executed. In a type-safe interpreted language, if the line is never executed, you still get the error (during parsing). But in a non-type-safe interpreted language, if the line is never executed, you don't get an error. – T.J. Crowder Sep 22 '16 at 15:41
  • I assume by *"parsing stage"* you mean a stage by which the code is checked for type errors before being executed. I have a related question: can a dynamically typed language (a language that allows you to change the type of variable at run-time) be type-safe? – user4582812 Sep 22 '16 at 15:53
  • @user4582812 I would think that that would be impossible. Imagine the type of variable is decided by the user at run time. How could the type system check the safety of such code? If would have to infer every possible type the variable could be; and that might not even be feasible in all scenarios. – Carcigenicate Sep 22 '16 at 18:39