What does “all legal JavaScript is legal TypeScript” mean?

All JavaScript code is legal TypeScript code

Here’s what this means:

  • TypeScript does not change the syntax of existing JavaScript
  • TypeScript does not change the behavior of existing JavaScript
  • TypeScript does consider some JS code to have type warnings, because that’s the point

Syntax

TypeScript will successfully parse all legal JavaScript code. It will emit this code as-is (minus downleveling, e.g. ES6 arrow functions will be converted to the equivalent ES5 code if you are targeting ES5 or lower). You may ignore type warnings generated by this JS code if you like; type warnings will not stop TypeScript from writing the output .js file.

Behavior

TypeScript does not change the behavior of existing JavaScript code, even though many people wish it did! Any JS code run through the compiler will behave the same as if it were run directly*.

Type Warnings

TypeScript may issue type warnings on code that it considers incorrect.

What does incorrect mean? Note that JavaScript is deterministic. Unlike C++, for example, it is basically impossible to cause behavior which is “implementation-defined”. JavaScript also very rarely throws exceptions; unlike other languages which might raise an exception when trying to multiply an object by a function, JS produces NaN. Accessing a property by a mispelled name will produce undefined instead of a runtime error (much to everyone’s chagrin). So the bar for “incorrect” here is intentionally set more strictly than “crashes your computer” or “throws an exception”.

Some code like this, for example, is all legal JavaScript that doesn’t even throw an exception. TypeScript considers each of these lines to have an error; this is typically considered a positive by those who use it:

var x = { } + 3; // Error, can't add objects and numbers
var y = "hello world".substr(1, 2, 3, 4, 5); // Error, too many parameters
var z = { x: 1, x: 2 }; // Error, duplicate property 'x'
var q = x[z]; // Error, indexing by an object doesn't really work...
var u = "hi".lenth; // Error, no property 'lenth' on string

But the spec!

A common counterargument goes like this

“The ECMAScript specification defines that Math.max coerces its arguments to a number, so it should be legal to call Math.max(someString, someOtherString)

It is true that the ECMAScript specification explicitly defines the coercions that take place at runtime. However, this logic doesn’t give us any actual insight. Taken at its face, this logic says that because all parameters are coerced to number at runtime, it should be legal to write

var x = Math.max("hello", window.setTimeout, { });

After all, this code does have defined behavior! But this misses the forest for the trees – it is plainly implausible that this code is correct for any reasonable definition of “correct”. TypeScript’s existence is predicated on the notion that some JavaScript code is not correct and you’d like to know about it.

The fact that the spec describes what happens is not instructive as to whether or not you’re writing a correct program. A doctor can clearly describe what will happen to you if you eat a rock, but that doesn’t mean rocks are food.

It’s a category error to look at the ECMAScript specification, which is designed to specify behavior, and decide that it is in fact a normative document which describes correct code. If you want, you can run with the nihilistic theory that “anything the spec defines should be OK” and use a variant of TypeScript which never issues type warnings. This variant is widely available, and is called “JavaScript”.


*: Exceptions to this rule are downlevelings which may have detectable differences due to limitations in the runtime, e.g. stack traces may be different

Leave a Comment