基本

ハンドブックの最初のページへようこそ。TypeScriptを初めて使う方は、'はじめに'ガイドから始めることをお勧めします。

JavaScriptのすべての値は、異なる操作を実行することで観察できる一連の動作を持っています。これは抽象的に聞こえますが、簡単な例として、messageという名前の変数に対して実行する可能性のあるいくつかの操作を考えてみましょう。

js
// Accessing the property 'toLowerCase'
// on 'message' and then calling it
message.toLowerCase();
// Calling 'message'
message();

これを分解すると、実行可能な最初のコード行はtoLowerCaseという名前のプロパティにアクセスし、それを呼び出します。2番目のコードはmessageを直接呼び出そうとします。

しかし、messageの値を知らないと仮定すると(これはよくあることですが)、このコードの実行を試みたときにどのような結果が得られるかを確実に言うことはできません。各操作の動作は、最初に持っていた値に完全に依存します。

  • messageは呼び出し可能ですか?
  • toLowerCaseというプロパティを持っていますか?
  • もしそうなら、toLowerCaseは呼び出し可能ですか?
  • これらの両方の値が呼び出し可能な場合、それらは何を返しますか?

これらの質問への答えは、通常、JavaScriptを書く際に頭の中に留めておくものであり、すべての詳細が正しいことを願う必要があります。

messageが次のように定義されていたとしましょう。

js
const message = "Hello World!";

おそらく推測できると思いますが、message.toLowerCase()を実行しようとすると、同じ文字列が小文字で返されます。

2行目のコードはどうでしょうか?JavaScriptに詳しい方は、これが例外で失敗することをご存知でしょう。

txt
TypeError: message is not a function

このような間違いを避けられると素晴らしいですね。

コードを実行するとき、JavaScriptランタイムが何をするかを選択する方法は、値の(どのような動作と能力を持っているか)を把握することです。それがTypeErrorが暗示していることの一部です。つまり、文字列"Hello World!"は関数として呼び出すことはできません。

プリミティブなstringnumberなどの一部の値については、typeof演算子を使用して実行時に型を識別できます。しかし、関数のような他のものについては、型を識別するための対応するランタイムメカニズムはありません。たとえば、次の関数を考えてみましょう。

js
function fn(x) {
return x.flip();
}

この関数は、呼び出し可能なflipプロパティを持つオブジェクトが与えられた場合にのみ機能することがコードを読めば観察できますが、JavaScriptはコードの実行中に確認できる方法でこの情報を公開していません。純粋なJavaScriptでfnが特定の値に対して何をするかを判断する唯一の方法は、それを呼び出して何が起こるかを確認することです。この種の動作は、コードを実行する前にコードが何をするかを予測することを困難にし、つまり、コードを書いているときにコードが何をするかを把握することがより困難になることを意味します。

このように見ると、とは、fnに渡すことができる値と、クラッシュする値を記述する概念です。JavaScriptは、何が起こるかを確認するためにコードを実行するという、動的型付けのみを実際に提供します。

代替手段は、コードの実行を開始する前に、コードが何をするかを予測するために静的型システムを使用することです。

静的型チェック

以前、関数としてstringを呼び出そうとしたときに発生したTypeErrorを思い出してください。ほとんどの人は、コードを実行したときにエラーが発生することを好みません。それらはバグと見なされます。そして、新しいコードを書くときは、新しいバグを導入しないように最善を尽くします。

少しだけコードを追加し、ファイルを保存し、コードを再実行してすぐにエラーが表示された場合、問題を迅速に特定できるかもしれません。しかし、常にそうとは限りません。機能を十分にテストしていなかったため、発生する可能性のあるエラーに実際には遭遇しない可能性があります。あるいは、幸運にもエラーを目撃した場合、大規模なリファクタリングを行い、掘り下げて確認しなければならない多くの異なるコードを追加してしまったかもしれません。

理想的には、コードを実行する前にこれらのバグを見つけるのに役立つツールがあればよいでしょう。それが、TypeScriptのような静的型チェッカーが行うことです。静的型システムは、プログラムを実行したときに値がどうなるかの形状と動作を記述します。TypeScriptのような型チェッカーは、その情報を使用して、何かがおかしくなりそうな場合に教えてくれます。

ts
const message = "hello!";
 
message();
This expression is not callable. Type 'String' has no call signatures.2349This expression is not callable. Type 'String' has no call signatures.
Try

TypeScriptで最後のサンプルを実行すると、最初にコードを実行する前にエラーメッセージが表示されます。

例外以外の失敗

これまで、ランタイムエラーのような特定の事柄について議論してきました。これは、JavaScriptランタイムが何かがおかしいと考えていることを示すケースです。これらのケースは、ECMAScript仕様に、予期しない事態が発生した場合に言語がどのように動作すべきかについての明示的な指示があるために発生します。

たとえば、仕様では、呼び出し可能でないものを呼び出そうとするとエラーが発生すると規定されています。これは「明白な動作」のように聞こえるかもしれませんが、オブジェクトに存在しないプロパティにアクセスした場合もエラーが発生するはずだと想像できるでしょう。代わりに、JavaScriptは異なる動作をし、値undefinedを返します。

js
const user = {
name: "Daniel",
age: 26,
};
user.location; // returns undefined

最終的に、静的型システムは、すぐにエラーをスローしない「有効な」JavaScriptであっても、システム内でエラーとしてフラグを立てるコードを判断する必要があります。TypeScriptでは、次のコードはlocationが定義されていないというエラーを生成します。

ts
const user = {
name: "Daniel",
age: 26,
};
 
user.location;
Property 'location' does not exist on type '{ name: string; age: number; }'.2339Property 'location' does not exist on type '{ name: string; age: number; }'.
Try

場合によっては、表現できるものにトレードオフが生じますが、その意図はプログラム内の正当なバグを捕捉することです。そして、TypeScriptは多くの正当なバグを捕捉します。

例:タイプミス、

ts
const announcement = "Hello World!";
 
// How quickly can you spot the typos?
announcement.toLocaleLowercase();
announcement.toLocalLowerCase();
 
// We probably meant to write this...
announcement.toLocaleLowerCase();
Try

呼び出されていない関数、

ts
function flipCoin() {
// Meant to be Math.random()
return Math.random < 0.5;
Operator '<' cannot be applied to types '() => number' and 'number'.2365Operator '<' cannot be applied to types '() => number' and 'number'.
}
Try

または基本的なロジックエラー。

ts
const value = Math.random() < 0.5 ? "a" : "b";
if (value !== "a") {
// ...
} else if (value === "b") {
This comparison appears to be unintentional because the types '"a"' and '"b"' have no overlap.2367This comparison appears to be unintentional because the types '"a"' and '"b"' have no overlap.
// Oops, unreachable
}
Try

ツール用の型

TypeScriptは、コードでミスをしたときにバグを捕捉できます。それは素晴らしいことですが、TypeScriptはさらに、最初にそれらのミスを犯すのを防ぐことができます。

型チェッカーには、変数やその他のプロパティで正しいプロパティにアクセスしているかどうかなどを確認するための情報があります。その情報があれば、使用したいプロパティを提案することもできます。

つまり、TypeScriptはコード編集にも活用でき、コア型チェッカーはエディターに入力中にエラーメッセージとコード補完を提供できます。これは、TypeScriptでのツールについて話すときによく言及されることの一部です。

ts
import express from "express";
const app = express();
 
app.get("/", function (req, res) {
res.sen
         
});
 
app.listen(3000);
Try

TypeScriptはツールを真剣に考えており、これは入力中の補完やエラーだけにとどまりません。TypeScriptをサポートするエディターは、エラーを自動的に修正する「クイックフィックス」、コードを簡単に再編成するためのリファクタリング、変数定義にジャンプしたり、特定の変数へのすべての参照を検索したりするための便利なナビゲーション機能を提供できます。これらはすべて型チェッカーに基づいて構築されており、完全にクロスプラットフォームであるため、お気に入りのエディターでTypeScriptのサポートが利用できる可能性が高いです

tsc、TypeScriptコンパイラー

型チェックについて話してきましたが、まだ型チェッカーを使用していません。新しい仲間であるtsc、TypeScriptコンパイラーを紹介しましょう。まず、npm経由で入手する必要があります。

sh
npm install -g typescript

これにより、TypeScriptコンパイラーtscがグローバルにインストールされます。ローカルのnode_modulesパッケージからtscを実行する場合は、npxまたは同様のツールを使用できます。

次に、空のフォルダーに移動して、最初のTypeScriptプログラムhello.tsを作成してみましょう。

ts
// Greets the world.
console.log("Hello world!");
Try

ここでは飾り気がないことに注意してください。この「ハローワールド」プログラムは、JavaScriptで「ハローワールド」プログラムを作成する場合と同じように見えます。そして、typescriptパッケージによってインストールされたコマンドtscを実行して型チェックしてみましょう。

sh
tsc hello.ts

ジャーン!

ちょっと待ってください。「ジャーン」とは一体ですか? tscを実行しましたが、何も起こりませんでした!型エラーはなかったため、報告するものがなかったため、コンソールには何も出力されませんでした。

ただし、もう一度確認してください。代わりに、ファイル出力が得られました。現在のディレクトリを見ると、hello.tsの隣にhello.jsファイルが表示されます。これは、tscがプレーンなJavaScriptファイルにコンパイルまたは変換した後のhello.tsファイルからの出力です。そして、内容を確認すると、TypeScriptが.tsファイルを処理した後に吐き出すものがわかります。

js
// Greets the world.
console.log("Hello world!");

この場合、TypeScriptが変換するものはほとんどなかったため、私たちが書いたものと同じように見えます。コンパイラーは、人が書くようなクリーンで読みやすいコードを生成しようとします。それは常に簡単ではありませんが、TypeScriptは一貫してインデントし、コードが異なるコード行にまたがっている場合に注意し、コメントを保持しようとします。

もし型チェックエラーを導入した場合どうでしょうか? hello.tsを書き直してみましょう。

ts
// This is an industrial-grade general-purpose greeter function:
function greet(person, date) {
console.log(`Hello ${person}, today is ${date}!`);
}
 
greet("Brendan");
Try

tsc hello.tsを再度実行すると、コマンドラインにエラーが表示されることに注意してください!

txt
Expected 2 arguments, but got 1.

TypeScriptは、greet関数に引数を渡すのを忘れたことを教えてくれます。当然のことです。これまで標準のJavaScriptしか記述していませんが、それでも型チェックによってコードの問題を見つけることができました。ありがとう、TypeScript!

エラー付きのエミット

最後の例で気づかなかったことが1つあります。hello.jsファイルが再び変更されたことです。そのファイルを開くと、内容はまだ基本的に入力ファイルと同じように見えることがわかります。これは、tscがコードに関するエラーを報告したという事実を考えると、少し驚きかもしれませんが、これはTypeScriptのコアバリューの1つに基づいています。多くの場合、あなたはTypeScriptよりもよく知っているでしょう。

以前から繰り返しているように、コードの型チェックは実行できるプログラムの種類を制限するため、型チェッカーが許容できるものにはトレードオフがあります。ほとんどの場合、それは問題ありませんが、それらのチェックが邪魔になるシナリオもあります。たとえば、JavaScriptコードをTypeScriptに移行し、型チェックエラーを導入していると想像してください。最終的には型チェッカーのために物事を整理することになるでしょうが、元のJavaScriptコードはすでに動作していました!TypeScriptに変換することで、実行が妨げられるべきでしょうか?

そのため、TypeScriptはあなたの邪魔をしません。もちろん、時間が経つにつれて、ミスに対してもう少し防御的になり、TypeScriptをより厳密に動作させたいと思うかもしれません。その場合は、noEmitOnErrorコンパイラーオプションを使用できます。hello.tsファイルを変更して、そのフラグ付きでtscを実行してみてください。

sh
tsc --noEmitOnError hello.ts

hello.jsが更新されないことに気づくでしょう。

明示的な型

これまで、TypeScriptにpersondateが何であるかを伝えていませんでした。コードを編集して、personstringであり、dateDateオブジェクトであるべきであることをTypeScriptに伝えてみましょう。また、datetoDateString()メソッドを使用します。

ts
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
Try

私たちが行ったのは、greetが呼び出し可能な値の型を記述するために、persondate型注釈を追加したことです。そのシグネチャは「greetは、型がstringpersonと型がDatedateを受け取る」と読むことができます。

これにより、TypeScriptはgreetが誤って呼び出された可能性のある他のケースについて教えてくれます。例えば…

ts
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
 
greet("Maddison", Date());
Argument of type 'string' is not assignable to parameter of type 'Date'.2345Argument of type 'string' is not assignable to parameter of type 'Date'.
Try

あれ? TypeScript が2番目の引数でエラーを報告していますが、なぜでしょう?

少し驚くかもしれませんが、JavaScriptでDate()を呼び出すと、stringが返されます。一方、new Date()Dateを構築すると、期待通りのものが得られます。

とにかく、エラーはすぐに修正できます。

ts
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
 
greet("Maddison", new Date());
Try

明示的な型注釈を常に書く必要はないということを覚えておいてください。多くの場合、TypeScriptは型を省略しても、型を*推論*(または「把握」)することができます。

ts
let msg = "hello there!";
let msg: string
Try

msgstring型であることをTypeScriptに伝えなくても、それを把握できました。これは便利な機能であり、型システムが結局同じ型を推論する場合は、注釈を追加しないのが最善です。

注:前のコード例のメッセージバブルは、エディターで単語の上にカーソルを合わせたときに表示されるものです。

型消去

上記の関数greettscでコンパイルしてJavaScriptを出力するとどうなるか見てみましょう。

ts
"use strict";
function greet(person, date) {
console.log("Hello ".concat(person, ", today is ").concat(date.toDateString(), "!"));
}
greet("Maddison", new Date());
 
Try

ここで2つの点に注意してください。

  1. persondateのパラメータには、型注釈がなくなりました。
  2. バッククォート(`文字)を使用した文字列である「テンプレート文字列」は、連結を伴うプレーンな文字列に変換されました。

2番目のポイントについては後で詳しく説明しますが、まずは最初のポイントに焦点を当てましょう。型注釈はJavaScript(厳密に言えばECMAScript)の一部ではないため、TypeScriptを未修正で実行できるブラウザやランタイムは実際には存在しません。これが、そもそもTypeScriptにコンパイラが必要な理由です。実行できるように、TypeScript固有のコードを削除または変換する方法が必要なのです。ほとんどのTypeScript固有のコードは消去され、同様に、ここでは型注釈が完全に消去されました。

覚えておいてください:型注釈は、プログラムの実行時の動作を絶対に変化させません。

ダウンレベル

上記のもう一つの違いは、テンプレート文字列が次のように書き換えられたことです。

js
`Hello ${person}, today is ${date.toDateString()}!`;

から

js
"Hello ".concat(person, ", today is ").concat(date.toDateString(), "!");

なぜこれが起こったのでしょうか?

テンプレート文字列は、ECMAScript 2015(別名ECMAScript 6、ES2015、ES6など - *聞かないでください*)というバージョンのECMAScriptの機能です。TypeScriptには、新しいバージョンのECMAScriptから、ECMAScript 3やECMAScript 5(別名ES3およびES5)などの古いバージョンにコードを書き換える機能があります。新しい、または「上位」バージョンのECMAScriptから、古い、または「下位」バージョンに移行するこのプロセスは、ダウンレベルと呼ばれることがあります。

デフォルトでは、TypeScriptはECMAScriptの非常に古いバージョンであるES3をターゲットにしています。targetオプションを使用すると、もう少し新しいものを選択できます。--target es2015を指定して実行すると、TypeScriptはECMAScript 2015をターゲットにするように変更され、つまり、コードはECMAScript 2015がサポートされている場所ならどこでも実行できるようになります。したがって、tsc --target es2015 hello.tsを実行すると、次の出力が得られます。

js
function greet(person, date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
greet("Maddison", new Date());

デフォルトのターゲットはES3ですが、現在のブラウザの大部分はES2015をサポートしています。したがって、一部の古いブラウザとの互換性が重要でない限り、ほとんどの開発者はターゲットとしてES2015以上を安全に指定できます。

厳密性

ユーザーによって、TypeScriptに求めるものは異なります。プログラムの一部のみを検証し、優れたツールを使用できるような、より緩やかなオプトインエクスペリエンスを求める人もいます。これがTypeScriptのデフォルトのエクスペリエンスであり、型はオプションであり、推論は最も寛容な型を取り、潜在的なnull/undefined値のチェックは行われません。tscがエラーに直面しても発行するのと同じように、これらのデフォルトは邪魔にならないように設定されています。既存のJavaScriptを移行する場合、これは望ましい最初のステップかもしれません。

対照的に、多くのユーザーはTypeScriptができるだけすぐに検証することを好むため、言語には厳密性の設定も用意されています。これらの厳密性の設定により、静的型チェックがスイッチ(コードがチェックされるかされないかのどちらか)からダイヤルに近いものに変わります。このダイヤルを回せば回すほど、TypeScriptはより多くのチェックを行います。これには少し余分な作業が必要になる場合がありますが、一般的に、長期的にはそれに見合う価値があり、より徹底的なチェックとより正確なツールが可能になります。可能な限り、新しいコードベースでは常にこれらの厳密性のチェックをオンにする必要があります。

TypeScriptには、オンまたはオフにできるいくつかの型チェックの厳密性フラグがあり、特に明記されていない限り、すべての例はそれらをすべて有効にした状態で記述されます。CLIのstrictフラグ、またはtsconfig.json"strict": trueは、それらをすべて同時にオンにしますが、個別にオフにすることもできます。知っておくべき2つの最も大きなものは、noImplicitAnystrictNullChecksです。

noImplicitAny

TypeScriptは、一部の場所では型を推論しようとせず、代わりに最も寛容な型であるanyにフォールバックすることを思い出してください。これは最悪の事態ではありません。結局のところ、anyにフォールバックするのは、まさにプレーンなJavaScriptのエクスペリエンスです。

ただし、anyを使用すると、そもそもTypeScriptを使用する目的が損なわれることがよくあります。プログラムの型付けが多いほど、検証とツールがより多くなり、コードを作成する際にバグが発生する回数が少なくなります。noImplicitAnyフラグをオンにすると、型が暗黙的にanyとして推論される変数でエラーが発行されます。

strictNullChecks

デフォルトでは、nullundefinedのような値は、他の任意の型に割り当てることができます。これにより、一部のコードを簡単に記述できますが、nullundefinedの処理を忘れると、世界中で数え切れないほどのバグが発生します。中には、それを10億ドルの間違いと考える人もいます。strictNullChecksフラグを使用すると、nullundefinedの処理がより明示的になり、nullundefinedの処理を*忘れたか*どうかを心配する必要がなくなります。

TypeScriptのドキュメントはオープンソースプロジェクトです。これらのページの改善にご協力ください。 プルリクエストを送信する

このページの貢献者
RCRyan Cavanaugh (55)
OTOrta Therox (13)
RTRich Trott (4)
DRDaniel Rosenwasser (4)
EBEli Barzilay (2)
17+

最終更新日:2024年3月21日