
As a JavaScript developer, you can program all day long, but not meet a single static type. So why think about studying them?
Well, in fact, learning about types is not just an exercise for developing thinking. If you invest some time in the study of static types, their advantages, disadvantages and examples of use, this can greatly improve your programming skills.
Interested? Then you are lucky - this is what our series of articles is about.
')
First, the definition
The easiest way to understand static types is to contrast them with a dynamic one. A language with static types is called a
language with static typing . On the other hand, a language with dynamic types is called a
language with dynamic typing .
The key difference is that languages ​​with
static typing perform type checking
at compile time , and
dynamically typed languages ​​perform type checking during program execution.
Here it remains to learn another concept: what does “
type checking ” mean?
To understand, look at the types of Java and JavaScript.
“
Types ” refers to the data type being defined.
For example, in Java you set
boolean
like this:
boolean result = true;
This value has the correct type, because the
boolean
annotation corresponds to the logical value specified in
result
, and not to an integer or something else.
On the other hand, if you try to declare:
boolean result = 123;
... this will not compile because the wrong type is specified. The code clearly indicates the result as a
boolean
, but sets it as an integer
123
.
JavaScript and other languages ​​with dynamic typing use a different approach, allowing the context to determine what type of data we are setting.
var result = true;
In short: static typed languages ​​require you to specify data types for your constructs before you can use them. Languages ​​with dynamic typing do not require. JavaScript takes the data type out of context, and Java requires it to be explicitly stated.
So you see, types allow you to define program
invariants , that is, logical statements and the conditions under which the program will run.
Type checking verifies and ensures that the type of the construction (constant, logical type, number, variable, array, object) matches the invariant that you defined. For example, you can specify that "this function always returns a string." When the program starts, you can safely assume that it will return a string.
The differences between static type checking and dynamic type checking are more important when a type error occurs. In a static-typed language, errors occur at compile time. In languages ​​with dynamic typing - only when the program starts. That is,
during the execution .
This means that a program in a dynamically typed language (like JavaScript or Python) can be compiled, even if it contains type errors.
On the other hand, if a program in a language with static typing (like Scala or C ++) contains type errors, it will not compile until errors are fixed.
New Era JavaScript
Since JavaScript is a dynamically typed language, you can safely declare variables, functions, objects, and anything else without declaring a type.
var myString = "my string"; var myNumber = 777; var myObject = { name: "Preethi", age: 26, }; function add(x, y) { return x + y; }
Convenient, but not always perfect. This is why tools such as
Flow and
TypeScript have recently appeared that give JavaScript * developers the option of using static types.
Flow is an open source static type checking library developed and released by Facebook. It allows you to gradually add types to your JavaScript code.
TypeScript , on the other hand, is a superset that compiles into JavaScript — although it feels TypeScript looks like a new language with static typing in itself. That is very similar to JavaScript and is not difficult to master.
In each case, if you want to use types, then explicitly tell the tool in which files to perform type checking. In the case of TypeScript you do, creating files with the extension
.ts
instead of
.js
. In the case of Flow, you specify the
@flow
comment at the beginning of the code.
Once you have declared that you want to perform type checking in a file, you can use the appropriate syntax to specify types. The difference between the tools is that Flow is a “controller” of types, not a compiler, and TypeScript, on the other hand, is a compiler.
I really think tools like Flow and TypeScript mark a
generational change and progress for JavaScript.
I personally learned a lot using types in my daily work. That is why I hope you will join me on this short and pleasant journey into the world of static types.
In the rest of this article will be considered:
Part 1. A small introduction to the syntax and language Flow (this part).
Parts 2 and 3. Advantages and disadvantages of static types (with detailed examples) .
Part 4. Do I need to use static types in javascript or not?Notice that in the examples for this article I chose Flow instead of TypeScript, because I know it better. For your own purposes, you can study them and choose a suitable tool. TypeScript is also fantastic.
Without further ado, let's get started!
Part 1. A Brief Introduction to Flow Syntax and Language
To understand the advantages and disadvantages of static types, you must first learn the basics of syntax for static types using Flow. If you have never worked in a language with static typing before, it may take some time to get used to the syntax.
Let's start by learning how to add types to JavaScript primitives, as well as constructs like arrays, objects, functions, etc.
boolean
This type in JavaScript describes boolean values ​​(true or false).
var isFetching: boolean = false;
Please note that the following syntax is always used when specifying a type:

number
This type describes floating-point numbers according to the IEEE 754 standard. Unlike many other programming languages, JavaScript does not distinguish between different types of numbers (such as integers, short, long, floating-point). Instead, all numbers are always stored as double precision numbers. Therefore, you only need one type to describe any number.
number
includes
Infinity
and
NaN
.
var luckyNumber: number = 10; var notSoLuckyNumber: number = NaN;
string
This type matches the string.
var myName: string = 'Preethi';
null
The data type is
null
in javascript.
var data: null = null;
void
The data type is
undefined
in javascript.
var data: void = undefined;
Note that
null
and
undefined
interpreted differently. If you try to write:
var data: void = null; 20: var data: void = null ^ null. This type is incompatible with 20: var data: void = null ^ undefined
Flow will give an error because the type assumed an
undefined
type, and this is not the same as the
null
type.
Array
Describes a JavaScript array. You use the
Array<T>
syntax to define an array whose elements have a certain type
<T>
.
var messages: Array<string> = ['hello', 'world', '!'];
Note that we replaced
T
with
string
. This means that we declare
messages
as an array of strings.
An object
Describes the JavaScript object. There are different ways to add types to objects.
You can add types to describe the shape of an object:
var aboutMe: { name: string, age: number } = { name: 'Preethi', age: 26, };
You can define an object as a map, in which the rows are assigned certain values:
var namesAndCities: { [name: string]: string } = { Preethi: 'San Francisco', Vivian: 'Palo Alto', };
You can also define an Object data type:
var someObject: Object = {}; someObject.name = {}; someObject.name.first = 'Preethi'; someObject.age = 26;
In the latter case, we can set any key and any value for an object without restrictions, so from the point of view of type checking there is no special meaning here.
any
It is literally any type. The
any
type effectively avoids any checks, so you should not use it unless absolutely necessary (for example, if you need to bypass type checking or need an emergency hatch).
var iCanBeAnything:any = 'LALA' + 2;
The
any
type can be useful if you use a third-party library that extends other system prototypes (like Object.prototype).
For example, if the library extends the Object.prototype with the
doSomething
property:
Object.prototype.someProperty('something');
then you may get an error:
41: Object.prototype.someProperty('something') ^^^^^^ property `someProperty`. Property not found in 41: Object.prototype.someProperty('something') ^^^^^^^^^^^^ Object
To avoid this, use
any
:
(Object.prototype: any).someProperty('something');
Functions
The most common way to add types to functions is to assign types to the arguments to be passed and (when appropriate) the return value:
var calculateArea = (radius: number): number => { return 3.14 * radius * radius };
You can even add types to async functions (see below) and generators:
async function amountExceedsPurchaseLimit( amount: number, getPurchaseLimit: () => Promise<number> ): Promise<boolean> { var limit = await getPurchaseLimit(); return limit > amount; }
Notice that our second parameter
getPurchaseLimit
described as a function that returns
Promise
. And the
amountExceedsPurchaseLimit
function
amountExceedsPurchaseLimit
also return a
Promise
, as described.
Type Aliases
Assigning type aliases is my favorite way to use static types. Ptsvedonyms allow you to create new types of existing types (number, string, etc.):
type PaymentMethod = { id: number, name: string, limit: number, };
Above we have created a new type called
PaymentMethod
, whose properties are composed of the
number
and
string
types.
Now, if you want to use
PaymentMethod
, you can write:
var myPaypal: PaymentMethod = { id: 123456, name: 'Preethi Paypal', limit: 10000, };
You can also create pseudonyms for any primitive by wrapping the underlying type inside another type. For example, if you want type aliases for
Name
and
Email
:
type Name = string; type Email = string; var myName: Name = 'Preethi'; var myEmail: Email = 'iam.preethi.k@gmail.com';
By doing this, you emphasize that
Name
and
Email
are two different things, not just strings. Since the name and mailing address do not really replace each other, now you will not confuse them by accident.
Parameterized Types
Parameterized types (Generics) is a level of abstraction over the types themselves. What is meant?
Let's get a look:
type GenericObject<T> = { key: T }; var numberT: GenericObject<number> = { key: 123 }; var stringT: GenericObject<string> = { key: "Preethi" }; var arrayT: GenericObject<Array<number>> = { key: [1, 2, 3] }
An abstraction for type
T
. Now you can use any type you want to represent
T
For
numberT
our
T
will be a number. And for
arrayT
, it will be of type
Array<number>
.
Yes I know. Your head is spinning a little if you are dealing with types for the first time. I promise that this “gentle” introduction is almost over!
Maybe
Maybe allows us to set a type for a value that could potentially be
null
or
undefined
. For some
T
will be set type
T|void|null
. This means that it can be either
T
, or
void
, or
null
. To establish the type of
maybe
you need to add a question mark before determining the type:
var message: ?string = null;
Here we say that the message is either a
string
, either
null
, or
undefined
.
You can also use maybe to indicate that a property of an object will either be of some type
T
, or
undefined
:
type Person = { firstName: string, middleInitial?: string, lastName: string, };
By placing a question mark after the
middleInitial
property
middleInitial
, you can specify that this is an optional field.
Non-intersecting sets
Another powerful way to present data models. Non-overlapping sets are useful if the program needs to process different data types simultaneously. In other words, the data format may depend on the situation.
Extend the PaymentMethod type from our previous examples on parameterized types. Suppose that a user in an application can have one of three payment methods. In this case, you can write something like this:
type Paypal = { id: number, type: 'Paypal' }; type CreditCard = { id: number, type: 'CreditCard' }; type Bank = { id: number, type: 'Bank' };
Then you can set the PaymentMethod type as a non-overlapping set of these three options.
type PaymentMethod = Paypal | CreditCard | Bank;
Now the payment method will always be one of three options. The set is assigned to "non-intersecting" due to the
type
property.
Further in the second part you will see more practical examples of non-intersecting sets.
So, almost finished. Let's just say a couple of other features of Flow that are worth mentioning.
1) Type inference : Flow tries to infer types wherever possible. This function is activated when the type controller is able to automatically display the data type in the expression. This helps to avoid unnecessary annotations.
For example, you can write:
class Rectangle { width: number; height: number; constructor(width, height) { this.width = width; this.height = height; } circumference() { return (this.width * 2) + (this.height * 2) } area() { return this.width * this.height; } }
Although there are no types in this class, Flow is able to adequately check them:
var rectangle = new Rectangle(10, 4); var area: string = rectangle.area();
Here I tried to set the
area
as a
string
, but in the definition of the
Rectangle
class, we found that
width
and
height
are numbers. Accordingly, by the definition of the
area
function, it must return a
number
. Suppose I did not explicitly define the types for the
area
function, Flow found an error.
One thing to note is that the flow maintainers recommend adding explicit definitions when exporting a class definition so that it is easier to later determine the cause of errors when the class is not used in a local context.
2) Dynamic type tests : Essentially this means that the Flow logic allows you to determine what type the value will have during program execution, so this information can be used during static analysis. Such tests are useful in situations where Flow finds an error, but you need to convince Flow that you are doing everything correctly.
I do not want to go into details, because it is a more advanced function, which I hope to write a separate article, but if you want to study it, you should study the
documentation .
We are done with the syntax.
We have considered a lot in the first part! Hope this superficial review was helpful and clear. If it is interesting to dig deeper, then I advise you to dive into
well-written documentation and study.
With the end of the syntax description, let's finally move on to the interesting part: studying the advantages and disadvantages of using types!
About the author: Preethi Kasireddy, Co-Founder and Lead Engineer, Sapien AI, CaliforniaContinued: "The advantages and disadvantages of static types"