TypeScript Yin-Yang - How TypeScript really compares to JavaScript
Once I started using TypeScript, after using JavaScript for several years, I often found that TypeScript documentation and blog posts were somewhat lacking.
Whereas TypeScript documentation and blog posts primarily focus on TypeScript syntax and its configurable features, they often fail to explain how these actually relate to standard JavaScript or what JavaScript problem they are trying to fix.
Sometimes it might even appear in blog posts that some new syntax is TypeScript specific (e.g. optional chaining), whereas it's actually just syntax coming from an ECMAScript proposal that often has already been supported by both Chrome and Babel for quite some time.
As a matter of fact, nearly all the syntax that is TypeScript specific does "not" allow you to actually program anything at runtime (e.g. interfaces, type annotations and conditional types), but solely exists to assist the static type checker of TypeScript at compile time. Exceptions to this rule are the syntactic sugar from the early days of TypeScript, being enums, constructor parameter properties and decorators.
In my opinion TypeScript is best to be seen as a static type checker. And the TypeScript specific syntax extensions are nothing more than a means to end.
Dynamically typed JavaScript
TypeScript was primarily created to counteract the dynamically typed nature of JavaScript.
The fact that JavaScript is dynamically typed is clearly demonstrated by the following example on MDN:
let foo = 42; // foo is now a number
foo = "bar"; // foo is now a string
foo = true; // foo is now a boolean
The sample code beautifully illustrates the following about JavaScript
- variables are not directly associated to any value type
- variables can be assigned (and re-assigned) values of all types
Actually, everything that 'holds a value' has "no" associated type in JavaScript:
- variable
- property
- class field
- parameter
- return value
To counteract the dynamically typed nature of JavaScript, TypeScript introduces static type checks based on the type that can be associated with TypeScript to everything that 'holds a value'.
Statically typed TypeScript
In TypeScript everything that ‘holds a value’ has an associated type. And whenever a value is assigned with an incorrect type, TypeScript will prohibit this through a compiler error.
Let’s assume that we intended the variable foo
to be a number
, then in
TypeScript we can add a type annotation to specify that:
let foo: number = 42;
In that case TypeScript will give compilation errors on lines 2 and 3:
foo = "bar";
// ~~~ TS2322: Type 'string' is not assignable to type 'number'.
foo = true;
// ~~~ TS2322: Type 'boolean' is not assignable to type 'number'.
The compilation errors above demonstrates that TypeScript is statically typed as opposed to JavaScript being dynamically typed.
But in this case we did not need to explicitly specify the number
type, since
TypeScript can infer in based on the assigned initial value of 42
:
let foo = 42; // %inferred-type: number
How to best describe TypeScript
In order to (better) understand TypeScript and how it compares to JavaScript, it might be useful to see how it's being described by others.
Traditionally TypeScript was often described as
"a typed superset of JavaScript".
This means that all JavaScript code is also valid TypeScript code. Which is
easily demonstrated by the fact that converting code to TypeScript takes nothing
more than changing the file extension from .js
to .ts
.
But TypeScript is only a superset when looking at the language syntax. However, semantically TypeScript programs behave more like a subset of JavaScript.
Whereas syntax-wise TypeScript is a superset of JavaScript, semantically not every TypeScript programs will be considered valid by the static type checker of TypeScript.

In order to better describe what TypeScript is, the typescriptlang.org website no longer describes TypeScript as being superset, but nowadays instead describes it as:
Typed JavaScript at Any Scale.
TypeScript extends JavaScript by adding types.
Although the description above of TypeScript is much better, in my opinion it's still lacking since it still fails to describe the actual purpose of TypeScript. Therefore, I personally much more prefer the description of TypeScript made by Tero Parviainen in its blog post about Angular 2:
It is just JavaScript - just with the seat belt fastened.
It doesn't really make sense to compare it to other languages.

This description nicely uses the metaphor of a fastened seat belt, to illustrate the fact that TypeScript is nothing more than JavaScript, but then safeguarded through its (static) type checker.
TypeScript Yin-Yang
In order to better understand TypeScript, I decided to start searching for a better matching analogy than being "typed superset" or a "fastened seat belt".
Without being an expert on the matter, it appeared to me that the ancient Chinese philosophy of Yin and Yang might be a matching analogy. And after a quick search, I found the following tweet that seems to confirm it:

Looking at Wikipedia, Yin-Yang is being described as follows:
…, yin and yang is a concept of dualism, describing how seemingly opposite or contrary forces may actually be complementary, interconnected, …
The dualism of Yin-Yang is also applicable when developing in TypeScript:
- the passive (compile-time / static) side of TypeScript (yin) interconnects with and is complementary to the active (runtime) side of JavaScript (yang)
- the Type System of TypeScript is modeled after the actual types occurring at runtime in JavaScript
- the code completion used while writing JavaScript, is facilitated by the Type System of TypeScript.
The analogy of Yin-Yang is only intended to explain how JavaScript and
TypeScript relate to each other while using TypeScript.
Without the context of developing with TypeScript the analogy of Yin-Yang will
not hold, because it's off-course perfectly possible (and also legitimate) to
develop in JavaScript without using TypeScript.
But even though I personally develop fulltime using TypeScript, I prefer seeing
myself as a JavaScript developer and not as a TypeScript developer.
To me developing in TypeScript is nothing more than another (popular) way of
writing JavaScript programs.
Side-by-side comparison of JavaScript and TypeScript
Using the analogy of Yin-Yang we can elegantly do a side-by-side comparison of TypeScript with JavaScript on a few key characteristics.
Run-time vs compile-time
- JavaScript: executes code at run-time
- TypeScript: checks types at compile-time
Basically, TypeScript is nothing more than an advanced type checker.
Besides some syntactic sugar from its early days (e.g. enums), the TypeScript
syntax extensions will be completely removed (type erasure) at compile time.
Therefore, it will have no runtime impact whatsoever.
Differences in how they are typed
- JavaScript: dynamically typed
- TypeScript: statically typed
As demonstrated earlier JavaScript is dynamically typed, meaning that a variable
(and all other things that ‘holds a value’) does not have a type associated with
it.
TypeScript however fixes the dynamically typed nature of JavaScript, but doing
static type checks during compilation.
ECMAScript proposals support
- JavaScript: early support for ECMAScript proposals
- TypeScript: late support for ECMAScript proposal (stage 3 / 4)
The reason for the existence of TypeScript is to introduce a compile-time (static) type-system to JavaScript. Therefore, there is not much priority in introducing new JavaScript language features to TypeScript.
Typically, new ECMAScript proposals are introduced once they reach stage 3 or 4, but sometimes new features are forgotten and only added to TypeScript after an issue is being logged.
Transpilation of modern / future ECMAScript syntax
- JavaScript: typically handled by Babel.js
- TypeScript: handled by either the TypeScript compiler or Babel.js
When developing in JavaScript you will typically use Babel.js to transpile / compile modern JavaScript syntax for browser versions that don’t support it.
When developing with TypeScript you can choose to use the TypeScript compiler to transpile / compile modern JavaScript syntax, or alternatively you use Babel.js (which is preferable IMO) to do the transpilation of both your JavaScript and TypeScript specific syntax.
Intrinsic (built-in) objects
- JavaScript: implements built-in (intrinsic) objects
- TypeScript: adds typings for built-in (intrinsic) objects
Besides language syntax, JavaScript also ships with an API offered by built-in
(intrinsic) objects like Date
and Object
.
TypeScript does not ship (nor implement) any additional JavaScript API, and instead only adds typings for built-in (intrinsic) objects.
NOTE: TypeScript does however offer a few built-in mapped types, but these do not have impact at runtime due to type erasure (explained later in this blog post)
Area of innovation
- JavaScript: innovates in language syntax
- TypeScript: innovates in the type space
Maybe the best illustration that TypeScript is complementary to JavaScript, is the area on which they innovate. Whereas JavaScript innovates on runtime language syntax, TypeScript exclusively innovates on the type space that only exists at compile time.
Therefore, all syntax that ‘does something at runtime’ is actually coming from JavaScript. Small exception to this rule is some syntactic sugar from the early days of TypeScript, like enums, constructor parameter properties and decorators.
Type System of TypeScript
As mentioned before, TypeScript is basically nothing more than a compile-time
type checker.
However, for the type checker to function it needs to have access to the type
related information of the code that needs to be checked.
To be able to feed this information to the type checker, TypeScript introduces a type system to the JavaScript language:
…, a type system is a logical system comprising a set of rules that assigns a property called a type to the various constructs of a computer program, such as variables, expressions, functions or modules.
The aim of the Type System of TypeScript is to model the runtime behaviour of
JavaScript.
Therefore, TypeScript possibly allows unwanted constructs because they are still
valid JavaScript:
const x = 4 + "2"; // evaluates to ‘42’
const y = "4" + 2; // evaluates to ‘42’
However, to prevent bugs (to some degree), TypeScript does prohibit some quirky
JavaScript constructs.
For instance the following is perfectly valid JavaScript, but will give
compilation errors in TypeScript:
const x = null + 42; // evaluates to 42 in JS
// ~~~~~~~~~ TS2365: Operator '+' cannot be applied to types 'null' and '42'.
const y = [] + 42; // evaluates to ‘42’ in JS
// ~~~~~~~ TS2365: Operator '+' cannot be applied to types 'undefined[]' and 'number'.
alert("foo", "bar"); // alerts ‘foo’ in JS
// ~~~~~ TS2554: Expected 0-1 arguments, but got 2.
Type erasure
It is an explicit design choice of TypeScript to have zero impact at runtime.
Therefore, type checking is only done at compile time.
To have zero impact in bundle size, TypeScript performs Type erasure during
compilation and removes all type information during compilation.
The only exception to this rule are classes (being standard JavaScript syntax)
and enums, which besides a type at compile time also exist as a value during
runtime.

Personally, I find the best illustration of Type erasure, to be the TypeScript support of Babel.js that basically does nothing more than removing all TypeScript specific syntax from your code.
A hopefully changed perspective on TypeScript
Now that this blog post is nearly coming to an end, I hope to have changed your
perspective on TypeScript and how it actually relates to JavaScript.
Hopefully the analogy of Yin-Yang also helps to understand how TypeScript really
relates to JavaScript.
Even when you still don't like TypeScript after reading this blog post, know that the Type System of TypeScript merely models the runtime behaviour of JavaScript.
Even though the TypeScript syntax extensions might make your code a bit more
verbose, they also make the types explicit in your code.
Whereas otherwise the types that exist at runtime would only exist implicitly in
the code or as, most likely outdated, documentation.
TS Yin-Yang: to be continued…
Hopefully you liked this blog post and its perspective on TypeScript versus JavaScript.
In my opinion TypeScript syntax / features are best to be described by comparing them with JavaScript, which I often find lacking in TypeScript related blog posts and documentation.
I'm really interested to hear if you found this blog post useful and / or if you would like to read more TS Yin-Yang blog posts in the future. You can reach me on Twitter at @EmilVanGalen.
But for now… let’s just say… to be continued.
(In Dutch) Online Divotion lunch sessie over TS Yin-Yang
Op woensdag 3 maart organiseert Divotion de digitale lunchsessie: TypeScript Yin-Yang van 12:00u tot 13:30u.
Tijdens deze lunchsessie willen we je een nieuwe blik geven door uit te leggen hoe TypeScript zich werkelijk verhoudt tot JavaScript. Dit met als ultieme doel om een beter begrip te krijgen van TypeScript.
Meer informatie over het programma en hoe aan te melden is hier te vinden.