JavaScriptで静的型を使用するのはなぜですか?(Flowを使用した静的型付けに関する4部構成の入門書)

JavaScript開発者は、静的な型に遭遇することなく、一日中コーディングできます。では、なぜわざわざそれらについて学ぶのでしょうか。

実は、タイプの学習は単なる心の練習ではなく、拡張です。静的型の長所、短所、および使用例について学ぶことに時間を費やしても構わないと思っているなら、それはあなたのプログラミングに非常に役立つかもしれません。

興味がある?幸運です—それがこの4部構成のシリーズの残りの部分です。

まず、定義

静的型を理解する最も簡単な方法は、静的型と対比することです。静的型を持つ言語は、静的型付き言語と呼ばれます。一方、動的型を持つ言語は、動的型付き言語と呼ばます。

主な違いは、静的に型付けされた言語はコンパイル時に型チェックを実行するのに対し、動的に型付けされた言語は実行時に型チェックを実行することです。

これにより、取り組むべきもう1つの概念が残ります。「型チェック」とはどういう意味ですか?

説明するために、JavaとJavascriptの型を見てみましょう。

「タイプ」とは、定義されているデータのタイプを指します。

たとえば、Javaで次のように定義した場合boolean

boolean result = true;

boolean注釈resultは整数などではなく、に指定された値と一致するため、これは正しい型です。

一方、宣言しようとした場合:

boolean result = 123;

…タイプが正しくないため、コンパイルに失敗します。また、明示的にマークresultとしてboolean、その後整数としてそれを定義123

JavaScriptやその他の動的型付け言語には異なるアプローチがあり、コンテキストで定義されているデータの種類を確立できます。

var result = true;

簡単に言うと、静的に型付けされた言語では、構成のデータ型を使用する前に宣言する必要があります。動的に型付けされた言語はそうではありません。JavaScriptはデータ型を意味しますが、Javaはそれを完全に述べています。

ご覧のとおり、タイプを使用すると、プログラムの不変条件、またはプログラムが実行される論理アサーションと条件を指定できます。

型チェックは、構成の型(定数、ブール値、数値、変数、配列、オブジェクト)が、指定した不変条件と一致することを確認および実施します。たとえば、「この関数は常に文字列を返す」と指定できます。プログラムが実行されると、文字列が返されると安全に想定できます。

静的型チェックと動的型チェックの違いは、型エラーが発生したときに最も重要になります。静的に型付けされた言語では、型エラーはコンパイルステップ中、つまりコンパイル時に発生します。動的型付け言語では、プログラムが実行されるとエラーが発生します。つまり、実行時に

つまり、動的に型指定された言語(JavaScriptやPythonなど)で記述されたプログラムは、スクリプトが正しく実行されないような型エラーが含まれている場合でもコンパイルできます。

一方、静的に型指定された言語(ScalaやC ++など)で記述されたプログラムに型エラーが含まれている場合、エラーが修正されるまでコンパイルに失敗します。

JavaScriptの新時代

JavaScriptは動的に型付けされた言語であるため、型を宣言せずに変数、関数、オブジェクトなどを宣言することができます。

便利ですが、常に理想的とは限りません。そのため、FlowやTypeScriptなどのツールは、JavaScript開発者に静的型を使用する*オプション*を提供するために最近介入しました。

Flowは、Facebookによって開発およびリリースされたオープンソースの静的型チェックライブラリであり、JavaScriptコードに型を段階的に追加できます。

一方、TypeScriptは、JavaScriptにコンパイルされるスーパーセットですが、それ自体が静的に型指定された新しい言語のように感じられます。とは言うものの、見た目も使い心地もJavaScriptと非常に似ており、理解するのは難しくありません。

いずれの場合も、タイプを使用する場合は、タイプチェックするファイルについてツールに明示的に指示します。TypeScriptの場合、これを行う.tsには、.js。の代わりに拡張子を付けてファイルを書き込みます。Flowの場合、ファイルの上にコメントを含めます。@flow

ファイルの型チェックを行うことを宣言すると、それぞれの構文を使用して型を定義できるようになります。2つのツールを区別する1つの違いは、Flowはタイプ「チェッカー」であり、コンパイラーではないということです。一方、TypeScriptはコンパイラです。

FlowやTypeScriptのようなツールは、JavaScriptの世代交代と進歩をもたらすと私は心から信じています。

個人的には、日々のタイプを使ってたくさんのことを学びました。だからこそ、静的タイプへのこの短くて甘い旅にあなたが参加してくれることを願っています。

この4部構成の投稿の残りの部分では、以下について説明します。

パートI.フローの構文と言語の簡単な紹介

パートII&III。静的タイプの長所と短所(詳細なウォークスルーの例を含む)

パートIV。JavaScriptで静的型を使用する必要がありますか?

この投稿の例では、TypeScriptに精通しているため、Flow overTypeScriptを選択したことに注意してください。あなた自身の目的のために、調査を行い、あなたに合ったツールを選んでください。TypeScriptも素晴らしいです。

それ以上の苦労なしに、始めましょう!

パート1:フローの構文と言語の簡単な紹介

静的型の長所と短所を理解するには、最初にFlowを使用して静的型の構文を基本的に理解する必要があります。これまで静的に型付けされた言語で作業したことがない場合は、構文に慣れるまでに少し時間がかかることがあります。

まず、JavaScriptプリミティブに型を追加する方法と、配列、オブジェクト、関数などの構造を調べることから始めましょう。

ブール値

これは、booleanJavaScriptの(trueまたはfalse)値を記述します。

タイプを指定する場合、使用する構文は次のとおりです。

これは、IEEE754浮動小数点数を表します。他の多くのプログラミング言語とは異なり、JavaScriptはさまざまな種類の数値(整数、短、長、浮動小数点など)を定義しません。代わりに、数値は常に倍精度浮動小数点数として格納されます。したがって、任意の番号を定義するために必要な番号タイプは1つだけです。

number含まれるInfinityNaN

ストリング

これは文字列を記述します。

ヌル

これは、nullJavaScriptのデータ型を記述します。

ボイド

これは、undefinedJavaScriptのデータ型を記述します。

nullとのundefined扱いが異なることに注意してください。あなたがやろうとした場合:

タイプvoidはタイプundefinedと同じではないタイプであると想定されているため、フローはエラーをスローしますnull

アレイ

JavaScript配列について説明します。構文Array<; T>を使用して、要素がT型である配列を記述します。

私が交換方法をお知らせTしてstring私は宣言してる意味し、messages文字列の配列として。

オブジェクト

これはJavaScriptオブジェクトについて説明しています。オブジェクトにタイプを追加するには、いくつかの異なる方法があります。

オブジェクトの形状を説明するタイプを追加できます。

文字列をある値にマップするマップとしてオブジェクトを定義できます。

オブジェクトをObjectタイプとして定義することもできます。

この最後のアプローチでは、オブジェクトに制限なしで任意のキーと値を設定できるため、型チェックに関する限り、実際にはあまり価値がありません。

どれか

これは文字通りあらゆるタイプを表すことができます。anyあなたは(あなたが型チェックのオプトアウトする必要があるか、エスケープハッチを必要とするときのように)絶対に必要でない限り、それを利用するのは避けるべきですので、タイプは、効果的にオフになっています。

any別のシステムのプロトタイプ(Object.prototypeなど)を拡張する外部ライブラリを使用する場合に役立つと思われる状況の1つです。

たとえば、Object.prototypeをdoSomethingプロパティで拡張するライブラリを使用している場合:

エラーが発生する場合があります。

これを回避するには、次を使用できますany

関数

関数に型を追加する最も一般的な方法は、入力引数と(関連する場合は)戻り値に型を追加することです。

非同期関数(以下を参照)とジェネレーターに型を追加することもできます。

2番目のパラメーターgetPurchaseLimitPromise。を返す関数としてどのように注釈が付けられているかに注目してください。またamountExceedsPurchaseLimit、を返すものとして注釈が付けられていPromiseます。

タイプエイリアス

型エイリアシングは、静的型を使用するための私のお気に入りの方法の1つです。これらを使用すると、既存のタイプ(数値、文字列など)を使用して新しいタイプを作成できます。

上記で、とタイプでPaymentMethod構成されるプロパティを持つという新しいタイプを作成しました。numberstring

このPaymentMethodタイプを使用する場合は、次のことができます。

基になる型を別の型にラップすることで、プリミティブの型エイリアスを作成することもできます。たとえば、エイリアスaNameEmailAddress:を入力する場合

これにより、あなたはそれを示しているNameEmail明確なものだけではなく、文字列です。名前と電子メールは実際には交換可能ではないため、これを行うことで、誤ってそれらを混同することを防ぎます。

ジェネリック

ジェネリックスは、型自体を抽象化する方法です。これは何を意味するのでしょうか?

見てみましょう:

タイプの抽象化を作成しましたT。これで、表現したいタイプを使用できますT。の場合numberTTはタイプnumberでした。一方、の場合arrayT、TはタイプでしたArrayer>.

Yes, I know. It’s dizzying stuff if this is the first time you’re looking at types. I promise the “gentle” intro is almost over!

Maybe

Maybe type allows us to type annotate a potentially null or undefined value. They have the type T|void|null for some type T, meaning it is either type T or it is undefined or null. To define a maybe type, you put a question mark in front of the type definition:

Here I’m saying that message is either a string, or it’s null or undefined.

You can also use maybe to indicate that an object property will be either of some type T or undefined:

By putting the ? next to the property name for middleInitial, you can indicate that this field is optional.

Disjoint unions

This is another powerful way to model data. Disjoint unions are useful when you have a program that needs to deal with different kinds of data all at once. In other words, the shape of the data can be different based on the situation.

Extending on the PaymentMethod type from our earlier generics example, let’s say that you have an app where users can have one of three types of payment methods. In this case, you can do something like:

Then you can define your PaymentMethod type as a disjoint union with three cases.

Payment method now can only ever be one of these three shapes. The property type is the property that makes the union type “disjoint”.

You’ll see more practical examples of disjoint union types later in part II.

All right, almost done. There are a couple other features of Flow worth mentioning before concluding this intro:

1) Type inference: Flow uses type inference where possible. Type inference kicks in when the type checker can automatically deduce the data type of an expression. This helps avoid excessive annotation.

For example, you can write:

Even though this Class doesn’t have types, Flow can adequately type check it:

Here I’ve tried to define area as a string, but in the Rectangle class definition we defined width and height as numbers. So based on the function definition for area, it must be return a number. Even though I didn’t explicitly define types for the area function, Flow caught the error.

One thing to note is that the Flow maintainers recommend that if you were exporting this class definition, you’d want to add explicit type definitions to make it easier to find the cause of errors when the class is not used in a local context.

2) Dynamic type tests: What this basically means is that Flow has logic to determine what the the type of a value will be at runtime and so is able to use that knowledge when performing static analysis. They become useful in situations like when Flow throws an error but you need to convince flow that what you’re doing is right.

I won’t go into too much detail because it’s more of an advanced feature that I hope to write about separately, but if you want to learn more, it’s worth reading through the docs.

We’re done with syntax

We covered a lot of ground in one section! I hope this high-level overview has been helpful and manageable. If you’re curious to go deeper, I encourage you to dive into the well-written docs and explore.

With syntax out of the way, let’s finally get to the fun part: exploring the advantages and disadvantages of using types!

Next up: Part 2 & 3.

Original text