Editor’s note: This post was edited on October 28, 2021 to add a new implementation, provide a better and easier-to-read comparison format, and discuss the different options more thoroughly.
JavaScript is a dynamically typed language, which means that data types of variables are determined by the value they hold at runtime and can change throughout the program as we assign different values to them.
There’s no inbuilt way to annotate or restrict the types of variables. While this means that we can write programs a little quicker without giving too much thought to the type a variable should have, it also means that our programs can get buggy and have unexpected behavior as a variable moves through different types.
To avoid these dynamic type-changing issues with JavaScript, we need to write our programs in another language and then transpile it to JavaScript. For this, we need a language that’s a typed extension of JavaScript to check and restrict the types of our variables.
There are several languages and libraries that provide statically typed extensions of JavaScript, such as:
In this article, we will focus on TypeScript, Flow, and Prop Types, comparing and contrasting their features and capabilities.
TypeScript | Flow | Prop Types | ||||
Developer | Microsoft | |||||
First released | 1 October 2012 | 18 November 2014 | 8 April 2017 | |||
License | Open source | MIT | MIT | |||
Community | Large | Small | Relatively small | |||
Dependencies | ||||||
Performance | Considered to be the fastest and least buggy | Some reported memory leakages. Flow team reported in 2019 that recheck time and memory usage were reduced | Unreported performance statistics | |||
Type checking | Uses JavaScript types, generic type definitions, and has optional type nullable | Uses JavaScript types, generic type definitions, and has optional type maybe | Uses its own type validator object | |||
Type casting | Uses keyword as | Uses symbol : | N/A | |||
Support for interfaces, enums and custom type objects | Supports interfaces and enums declared with keywords interface and enum | Supports interfaces and enums, declared with keywords interface and enum | Supports enum using its own option, PropTypes.oneOf([…]) | |||
Text editor support | Massive support with several editors/IDEs providing functionality like IntelliSense (autocomplete), go-to definition, errors/warnings, etc. | Massive support with several editors/IDEs providing functionality like IntelliSense (autocomplete), go-to definition, errors/warnings, etc. | Limited support with Visual Studio Code and WebStorm to autogenerate propTypes of components | |||
Resources and Documentation | ||||||
Community and project support | Supported by several frameworks and libraries out of the box, e.g., Vue, React, Angular, Express, etc., including Nest.js | Can be installed on existing projects via Babel. Most common use is with React. | Primarily used with React (where it was extracted from) but can be used with other frameworks by manually calling PropTypes.checkPropTypes | |||
Questions posted on Stack Overflow | Over 100,000+ posts | about 500 posts | about 500 posts | |||
Compiler error detection | Available in IDEs and text editors | AvaIlable in supported IDEs and editors | Not available | |||
Syntax | Comprehensive type checking, includes both static and dynamic type annotations | Comprehensive type checking, includes both static and dynamic type annotations | Relies on reference to PropTypes object | |||
Generics | Supported | Supported | Not supported | |||
Support in existing projects | TypeScript package can be added to support TypeScript | Add support with Babel | Add support by installing library and manually calling PropTypes.checkPropTypes |
In this article, we will compare and contrast these JavaScript-extending technologies across a number of features and abilities.
As we explore their similarities and differences, an important difference to note upfront is that unlike TypeScript, Flow and PropTypes are not programming languages. Flow and PropTypes are only static type checking libraries, and his difference impacts what frameworks support their use.
TypeScript has the most library support of the three technologies compared in this article, and setting projects in Vue, Angular, or React to use TypeScript is easy and supported out of the box.
For example, we can build a Vue project with TypeScript by choosing the TypeScript option from the Vue CLI because it is built-in. Once we choose TypeScript, then we can configure it to transpile to JavaScript in our tsconfig.json
file (usually setting ECMAScript 2009 to maximize the support of browsers).
// tsconfig.json { "compilerOptions": { // this aligns with Vue's browser support "target": "es5", // this enables stricter inference for data properties on `this` "strict": true, // if using webpack 2+ or rollup, to leverage tree shaking: "module": "es2015", "moduleResolution": "node" } }
Then, when we reference Vue during the component creation, we’ll get TypeScript type inference:
import Vue from 'vue' const Component = Vue.extend({ // ... })
Also, with Vue, we can write components with TypeScript in its own special way with classes, like this:
<template> <button @click="onClick">Click!</button> </template> <script lang="ts"> import * as Vue from "vue"; import { Component } from "vue-property-decorator"; @Component() class App extends Vue { public message: string = "Hello World"; public onClick(): void { window.alert(this.message); } } export default App; </script>
Here, we can see the familiar JavaScript syntax with some TypeScript annotations. For example, Angular is built with TypeScript and it uses TypeScript almost exclusively. There was once an option to write Angular apps with plain JavaScript, but it never caught on.
On the other hand, we can add Flow to our project by adding Flow support in the Babel configuration to enable support for Flow with Babel.
To add Flow support, we just run:
npm install --save-dev @babel/preset-flow
and add the following to the .babelrc
file:
{ "presets": ["@babel/preset-flow"] }
To add PropTypes in our project, we only have to install the PropTypes library, define our component prop types, and manually call the PropTypes.checkPropTypes
validation method.
To install PropTypes, we run:
npm install --save prop-types
Then we define our component prop type validations, set an object with the properties/values:
const myPropTypes = { name: PropTypes.string, age: PropTypes.number, // ... define your prop validations }; const props = { name: 'hello', // is valid age: 'world', // not valid };
And run the PropType check:
// Let's say your component is called 'MyComponent' // Works with standalone PropTypes PropTypes.checkPropTypes(myPropTypes, props, 'age', 'MyComponent'); // This will warn as follows: // Warning: Failed prop type: Invalid prop `age` of type `string` supplied to // `MyComponent`, expected `number`.
As mentioned earlier, all three technologies provide type checks and restrictions to variables, and here we will compare them.
Flow and TypeScript have significantly similar type definition syntaxes. They both:
nullable
type while Flow has the maybe
typeFor an example of the last bullet, see below:
function identity<T>(value: T): T { return value }
The generic takes a variable type that can be overloaded with different types, i.e., identity<string>('foo')
.
Moreover, the following code block is the same in both TypeScript and Flow, where the function takes two variables of type string
and returns them concatenated together.
function concat(a: string, b: string) { return a + b; }
PropTypes, on the other hand, uses its own type validator for setting the types of component properties — i.e., in PropTypes, we would define a type using the PropTypes
object.
import React from 'react'; import PropTypes from 'prop-types' class MyComponent extends React.Component { render() { // ... do things with the props } } MyComponent.propTypes = { optionalArray: PropTypes.array, ...
Both Flow and TypeScript provide the ability to explicitly cast a variable from one type to another.
With Flow, we cast using the symbol :
, while in TypeScript we cast using the keyword as
.
// TypeScript let value = 1 as number; // Flow let value = 1; (value: number);
PropTypes does not provide the ability to cast a property or variable from one type to another.
Interfaces are type definitions that restrict the structure and properties of an object. Enums are listed values and can be used as a type by restricting variables to holding one of its values. Flow, TypeScript, and PropTypes allow the definition of custom type restrictions.
Flow and TypeScript define these custom type objects using the keyword interface
. They both also allow classes to inherit an interface’s structure using the implements
keyword. See an example below.
interface PersonInterface { firstName: string; lastName: string; fullName(firstName: string, lastName: string): string } class Person implements PersonInterface { firstName: string; lastName: string; constructor(firstName: string, lastName: string){ this.firstName = firstName; this.lastName = lastName; } fullName(firstName: string, lastName: string): string { return `${firstName} ${lastName}`; } }
We’ll get an error with the Flow and TypeScript transpilers if we didn’t implement all the members listed in the interface.
Flow and TypeScript also define enum lists for type checking using the enum
keyword.
enum Status { Active, Paused, Off, } const status: Status = Status.Active //correct const wrongStatus: Status = 'test' //incorrect and not conforming to defined type
One unique feature of Flow is that we can put our type checking code in the comments like this:
function greet(greeting /*: string*/) /* : string */ { return greeting; }
This isn’t available with TypeScript. However, this isn’t a big draw since comments don’t have autocomplete or syntax highlighting.
PropTypes, on the other hand, defines enum typing using PropTypes.oneOf
like so:
... optionalEnum: PropTypes.oneOf(['News', 'Photos']), ...
PropTypes also defines object type structures using PropTypes.shape
.
... optionalObjectWithShape: PropTypes.shape({ optionalProperty: PropTypes.string, requiredProperty: PropTypes.number.isRequired }), ...
Both Flow and TypeScript have inbuilt or plugin support from editors like Visual Studio Code and Sublime.
Editor support for Flow and TypeScript means that developers get functionality like:
There is also documentation to install Flow support in an existing project via Babel. Read further details of editor/IDE installations for Flow support and TypeScript editor support.
PropTypes also has some support on Visual Studio Code for automatically generating the prop types of components for their assigned values.
PropTypes also has the ability to be applied to other libraries by calling PropTypes.checkPropTypes
manually (it is called automatically in React). Though support for other libraries is mentioned in the official documentation of PropTypes, there is not a lot of external documentation on how this works for different libraries.
The performance difference between Flow and TypeScript is minimal. TypeScript consistently uses 500-600MB of RAM, and Flow isn’t too different. There isn’t much difference between them in terms of computing resource efficiency.
PropTypes efficiency? According to this Reddit thread, TypeScript is faster and less buggy.
Community support for TypeScript is the largest when compared with support for Flow and PropTypes. Flow and PropTypes also have less documentation aside from the official sources, and fewer questions on StackOverflow, as we previously discussed in the table at the top of this article.
Many popular frontend libraries, frameworks, and platforms have inbuilt support for TypeScript, such as Vue, Angular, and React. Node.js web frameworks like Express can also have TypeScript support added, and Nest.js is built with TypeScript support baked in, allowing us to use TypeScript for development without making big changes to our project.
For example, to use TypeScript with Express, we just have to run the following command to install the TypeScript transpiler.
npm install --save-dev typescript
Then we can add a tsconfig.json
file to configure our TypeScript transpiler by writing something like:
{ "compilerOptions": { "module": "commonjs", "esModuleInterop": true, "target": "es6", "noImplicitAny": true, "moduleResolution": "node", "sourceMap": true, "outDir": "dist", "baseUrl": ".", "paths": { "*": [ "node_modules/*" ] } }, "include": [ "src/**/*" ] }
To install the type definitions for Express and Node’s standard library, we run:
npm install --save-dev @types/node @types/express
As we can see, it’s not too hard to use TypeScript with existing libraries because TypeScript type definitions exist for many libraries and frameworks. For more type definitions for TypeScript, we can go to the DefinitelyTyped website.
TypeScript definitely wins in terms of the quantity of resources and documentation available.
TypeScript offers:
As an added benefit, the TypeScript playground supports different versions of TypeScript so we can see how our code acts with different versions of the TypeScript transpiler.
Since TypeScript is supported by many libraries and frameworks, lots of people are using TypeScript daily. There is plenty of information about each feature in TypeScript, including examples and explanations. It is most likely your best option for implementing static typing.
On the other hand, Flow only has some documentation and a short document on how to set up Flow with React, which can also be applied to setting up Flow on other JavaScript frameworks. By far, however, PropTypes has the sparsest documentation of the three options, though a few posts such as this exist.
LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
8 Replies to "Comparing statically typed JavaScript implementations"
After writing writing a few reasonably sized applications with flow, I’ve got to say I’m a fan and haven’t run into many of the cons you list in the article (my IDE supports it, it has a decent community, and there are community-created types for popular libraries).
For me, flow’s biggest benefits are that it’s easy add (can be done incrementally) and remove, and it works with every system out of box.
I don’t know why people compare TypeScript so much with Flow. Both of them are great, for different teams. In 2017 we built our new frontend technology with Angular and TS but we encountered difficulties shortly, especially when it came to debugging Angular (we are mostly backend devs, inexperienced in frontend). But we found ourselves less productive in TS (even if it’s closer to the type system we are used to in Java) and didn’t even use it in a strict way, we couldn’t see the benefits to pursue it further. Because of that, at the end of 2019 we have adopted a new stack, React with Hooks and Flow. It’s just that it is more suitable for our team, the features are delivered faster, a good choice for our 3-man army on the frontend. I recommend people to experiment with both and please don’t disconsider Flow, it is pretty powerful. We got sold on the idea that TS is for Java devs, but this wasn’t true in our case. It could be in yours. Moreover, we are using IntelliJ for everything JavaScript and I have to say the Ultimate version has strong Flow support, didn’t let us down.
`flow` tag in S.O. has nothing to do with the Facebook library, as it was wrongly stated in this article.
For that purpose, `flowtype` is used:
https://stackoverflow.com/questions/tagged/flowtype
“Flow checks types locally and doesn’t have a language server or type definitions like TypeScript. We need to run the Flow compiler to check our app, whereas we can check our JavaScript code with a TypeScript language server as we’re typing code into our files.”
This is completely wrong. Flow has always been able to do the type checking in real time (look for “flow check-contents”). Same as TS Flow has plugins for popular editors and IDE’s (https://flow.org/en/docs/editors/) which are using this API.
A really biased comparison, this would seem to me. I have used both flow and TypeScript extensively and can concur with previous replies that:
– Flow is easier to integrate incrementally into an existing codebase thatn TypScript.
– It is well supported in Webstorm, PhpStorm and other JetBrain products. There are several good plugins for VSCode as well, and they can give real time flow checking too.
– Community support is fine enough. To be honest, I’ve had more trouble finding solutions to TypeScript issues than I’ve had with Flow. But that may be personal.
– The argument that libraries do not support it is a bit weird. Never looked at https://github.com/flow-typed/flow-typed/tree/master/definitions/npm?
I agree with those above. This is a super completely bias comparison from someone who hasn’t put enough effort into flow to create a fair comparison.
– React does not offer TS out of the box, but it does for Flow because React defs are built directly into the Flow base defs. I think you may be confusing React from CRA, which are two completely different things.
– The licenses you specified can have the wrong impression saying that Flow’s license is `Facebook`? They’re both MIT
– Your development experience comparison also shows the lack of research. Yes there are less libraries that are shipped with flowtype by default but just like TS, these are added to the flow-typed library.
– Your tutorial for installation of Flow should have at least given instructions to install `flow-bin`. Which is the equivalent to the `typescript` package in flow world.
For those who are reading the comments to learn more here are my 2 cents.
I think it all depends on what you’re trying to accomplish and what your stack is. If you’re coming from a node or react background, flow may work for you.
Flow’s main driving force is soundness, which helps you prevent runtime errors through type checking and statically analysing your code.
Its focus is to prevent runtime errors but of course because it always compiles down to Javascript types can always be typed wrong as its done by humans, it doesn’t block compiling. In that case I recommend putting lint-staged so that you at least don’t comment code with flow errors.
I don’t really get how PropTypes is part of this article. PropTypes is NOT a static typing system – it tests the types on runtime. It also built specifically for React component props checking. A very narrow usage.
It feels that someone needed here to compare 3 systems so PropTypes was thrown into the mix.
Strongly agree. The scopes of (Typescript,Flow) are so vastly different than PropTypes.