📜 ⬆️ ⬇️

Why use static types in javascript? (Example of static typing on Flow)

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; /*------------------------FLOW ERROR------------------------*/ 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; // 'LALA2' 

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'); // No errors! 

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:

 /* @flow */ 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(); // Flow errors 100: var area: string = rectangle.area(); ^^^^^^^^^^^^^^^^ number. This type is incompatible with 100: var area: string = rectangle.area(); ^^^^^^ string 

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, California
Continued: "The advantages and disadvantages of static types"

Source: https://habr.com/ru/post/326304/


All Articles