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.
The Replay is a weekly newsletter for dev and engineering leaders.
Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.
| 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 lets you replay user sessions, eliminating guesswork by showing exactly what users experienced. It captures console logs, errors, network requests, and pixel-perfect DOM recordings — compatible with all frameworks, and with plugins to log additional context from Redux, Vuex, and @ngrx/store.
With Galileo AI, you can instantly identify and explain user struggles with automated monitoring of your entire product experience.
Modernize how you understand your web and mobile apps — start monitoring for free.

Build a CRUD REST API with Node.js, Express, and PostgreSQL, then modernize it with ES modules, async/await, built-in Express middleware, and safer config handling.

Discover what’s new in The Replay, LogRocket’s newsletter for dev and engineering leaders, in the March 25th issue.

Discover a practical framework for redesigning your senior developer hiring process to screen for real diagnostic skill.

I tested the Speculation Rules API in a real project to see if it actually improves navigation speed. Here’s what worked, what didn’t, and where it’s worth using.
Hey there, want to help make our blog better?
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 now