TypeScript
TypeScriptは、JavaScriptのスーパーセットです。TypeScriptは、静的型付け、クラス、インターフェース、継承などの機能をJavaScriptに加えます。 これにより、開発者はより大規模なプロジェクトでの開発をより効率的に行うことができます。 本チュートリアルでは、TypeScriptの基本的な構文、データ型、関数、クラス、インターフェースなどについて学習できます。
TypeScriptの概要
編集TypeScriptの概要
編集TypeScriptは、マイクロソフトによって開発された、JavaScriptのスーパーセット言語であり、JavaScriptに対して型システムやクラス、インターフェイス、ジェネリクスなどの静的な機能を追加しています。TypeScriptは、JavaScriptの開発をより安全かつ効率的にするために設計されています。
TypeScriptの特徴
編集TypeScriptの主な特徴は以下の通りです。
- 静的型付け:変数や関数の型を宣言し、コンパイル時に型のチェックを行うことができます。
- クラスやインターフェイスのサポート:オブジェクト指向プログラミングをサポートし、コードの再利用性や保守性を高めることができます。
- コンパイル時のエラーチェック:コンパイル時にエラーチェックを行うことで、ランタイムエラーを事前に防止することができます。
- ES6/ES2015以降の構文のサポート:TypeScriptはES6/ES2015以降の構文をサポートしており、コードをより簡潔かつ読みやすくすることができます。
JavaScriptとの違い
編集JavaScriptは動的型付けの言語であり、変数の型を宣言する必要がありません。一方、TypeScriptは静的型付けの言語であり、変数や関数の型を宣言することができます。
JavaScriptはオブジェクト指向プログラミングをサポートしていますが、クラスやインターフェイスの宣言方法は限定的です。一方、TypeScriptはオブジェクト指向プログラミングをより豊富にサポートしており、クラスやインターフェイスの宣言がより柔軟になっています。
JavaScriptはコンパイルエラーを実行時に発生させるため、開発者がエラーを特定するのが難しい場合があります。一方、TypeScriptはコンパイル時にエラーチェックを行うため、開発者は実行時にエラーが発生することを事前に防止することができます。
TypeScriptのインストール
編集以下は、Node.js、Deno、およびTypeScript PlaygroundでTypeScriptをインストールおよびビルドする方法です。
- Node.js
-
- Node.jsをインストールします。
- ターミナルで、
npm install -g typescript
を実行して、TypeScriptをグローバルにインストールします。 tsc
コマンドを実行して、TypeScriptをコンパイルします。
- Deno
-
- Denoをインストールします。
- ターミナルで、
deno install -n ts https://deno.land/std/typescript/tsc.ts
を実行して、TypeScriptコンパイラをインストールします。 - TypeScriptファイルを作成し、
ts
コマンドを使用してコンパイルします。例:ts file.ts
- TypeScript Playground
-
- TypeScript Playgroundにアクセスします( https://www.typescriptlang.org/play )。
- 左側のペインにTypeScriptコードを入力します。
- 右側のペインには、JavaScriptにコンパイルされたコードが表示されます。また、コンパイルエラーがある場合は、そのエラーが表示されます。
それぞれの特徴:
- Node.js
- Node.jsはJavaScriptを使用してサーバーサイドアプリケーションを開発するためのオープンソースのランタイム環境であり、GoogleのV8 JavaScriptエンジンを基に構築されています。非同期I/O処理、イベント駆動アーキテクチャ、およびモジュールベースのアプローチなどの特徴を備えています。
- 特にWebアプリケーションの開発において優れた選択肢となっており、サーバーサイドのJavaScriptを実行することで、クライアント側とサーバー側のコードを同じ言語で統一できます。また、Node.jsは多くのパッケージやライブラリが提供されており、開発の効率性が向上しています。
- Node.jsはフロントエンドのJavaScriptフレームワークと組み合わせて、ReactやAngularなどの人気フレームワークと連携することで、フルスタックのWebアプリケーションを開発することが可能です。
- 多くの企業や開発者によって採用され、オープンソースのプロジェクトとしてコミュニティによる開発が進められています。その結果、Node.jsは高い人気を誇り、柔軟性と拡張性に優れた選択肢として広く活用されています。
- Deno
- Denoは、オープンソースのランタイム環境であり、JavaScriptやTypeScriptを使用してサーバーサイドアプリケーションを開発するためのプラットフォームです。Node.jsと同様にGoogleのV8 JavaScriptエンジンを採用していますが、非同期I/O処理とイベント駆動アーキテクチャの代わりに、シンプルで安全なAPIを強調しています。
- Denoは、Node.jsと比較してよりセキュアなデフォルト設定を採用しており、組み込まれたパッケージマネージャーによって外部の依存関係を管理しています。また、TypeScriptのサポートにおいても優れており、標準でTypeScriptをサポートしています。
- 異なるアーキテクチャとAPIを提供するため、Denoの使用には学習コストが発生しますが、セキュリティや依存関係の管理において強力な機能を提供しています。そのため、特にセキュリティ上の懸念がある企業やプロジェクトにとって適した選択肢とされています。
- TypeScript Playground( https://www.typescriptlang.org/play )
- TypeScript Playgroundは、オンラインツールで、ブラウザ上でTypeScriptのコードを作成し、それをコンパイルして実行結果を確認するための手段です。JavaScript開発者がTypeScriptの構文、型、および機能を探索し、学ぶための便利なリソースの一つです。Playgroundを使用することで、TypeScriptのコードを試し、実際にどのように動作するかを確認することができます。
- 左側にはTypeScriptコードを入力するエディターがあり、コードの入力後には右側にJavaScriptコードのプレビューが表示されます。また、TypeScriptファイルを読み込むことや、ファイルをアップロードしてプレビューを確認することも可能です。
- TypeScript Playgroundは、TypeScriptの基本を学ぶための有益なツールであり、導入前に実際にコードを書いて、そのコンパイル結果を確認することができます。これにより、開発者は直感的にTypeScriptの特徴や動作を理解し、学習プロセスを効果的に進めることができます。
具体的には、トランスパイラは以下のような用途で使用されることがあります:
- 言語の変換: あるプログラミング言語で書かれたコードを別のプログラミング言語に変換することがあります。例えば、TypeScriptからJavaScriptへの変換、CoffeeScriptからJavaScriptへの変換などが挙げられます。
- バージョン間の互換性: 新しい言語のバージョンがリリースされた際、既存のコードを新しいバージョンに対応させるための変換が必要です。トランスパイラは、古いバージョンのコードを新しいバージョンに変換する手段を提供します。
- 構文の最適化: コードをより効率的に実行するために、トランスパイラは構文やコード構造を最適化することがあります。これにより、コードの実行速度が向上したり、特定のルールに基づいて変換が行われたりします。
TypeScriptのコードの直接実行
編集ts-nodeは、Node.jsのランタイム環境でTypeScriptコードを実行するためのツールです。Node.jsは、JavaScriptを実行するためのランタイム環境であるため、TypeScriptのコードを直接実行することはできませんが、ts-nodeを使用することで、TypeScriptのコードをトランスパイルせずに直接実行することができます。
ts-nodeは、TypeScriptの実行を容易にするために開発され、TypeScriptファイルをコンパイルし、実行する必要がないため、より速く開発することができます。また、TypeScriptの実行に必要な設定ファイルや依存関係の管理も自動的に行われます。
ts-nodeは、グローバルにインストールすることができ、コマンドラインから単一のファイルまたはディレクトリを実行することができます。また、ts-nodeは、Node.js REPL(Read-Eval-Print Loop)にも統合されており、TypeScriptのスニペットをインタラクティブに実行することができます。
% npx ts-node ファイル名.ts
だけでコンパイルと実行が行われ、ソースコードが変更されていない場合は、キャッシュ上のコンパイル後の .js を直接実行します。
ts-nodeは、TypeScriptプロジェクトの開発をより迅速かつ簡単にするための便利なツールであり、特にNode.jsとTypeScriptを組み合わせた開発において、大きな利点を提供します。
TypeScriptの基本文法
編集// 変数と定数 let num: number = 123; // 数値型の変数 const str: string = "Hello TypeScript!"; // 文字列型の定数 var flag: boolean = true; // 真偽値型の変数 // 型の宣言 type User = { name: string; age: number; } const user: User = { name: "Alice", age: 20 }; // User型のオブジェクト // 関数とクラス function greet(name: string): void { console.log(`Hello, ${name}!`); } class Animal { name: string; constructor(name: string) { this.name = name; } sayHello(): void { console.log(`Hello, I'm ${this.name}.`); } } const cat: Animal = new Animal("Tama"); // Animalクラスのインスタンス cat.sayHello(); // インターフェイスとジェネリックス interface Person<T> { name: string; age: T; } const person: Person<number> = { name: "Bob", age: 25 }; // Person型のオブジェクト // 制御構文 /// 分岐 if (flag) { console.log("This is true."); } else { console.log("This is false."); } switch (num) { case 0: console.log("zero"); break; case 1: console.log("one"); break; default: console.log("other"); break; } /// 繰り返し for (let i = 0; i < num; i++) { console.log(i); } let i = 0; while (i < num) { console.log(i); i++; }
このコードの中で、TypeScriptとJavaScriptの主な違いは変数、定数、関数、クラス、インターフェース、ジェネリックス、および制御構文の宣言に型情報を含んでいることです。
具体的には、変数の宣言には型アノテーションが含まれています。JavaScriptでは、変数の宣言時に型を指定する必要はありません。
また、型の宣言にはTypeScriptで導入されたtypeキーワードが使用されており、JavaScriptには存在しません。
関数の引数と返り値に型情報が含まれており、関数が返す値の型を指定する必要があります。JavaScriptでは、関数の引数や返り値に型を指定する必要はありません。
クラスのメンバー変数やメソッドにも型情報が含まれており、JavaScriptではクラスのメンバーに型を指定する方法がありません。
インターフェイスにはジェネリック型パラメータが含まれており、TypeScriptにはジェネリック型パラメータをサポートする機能がありますが、JavaScriptには存在しません。
制御構文は、if-else分岐、switch文、forループ、whileループが含まれていますが、JavaScriptとの違いはありません。ただし、ループ内で使用される変数については、TypeScriptでは事前に型を指定する必要があります。
変数と型
編集TypeScriptでは、変数に対して型を指定することができます。これにより、コードの安全性が向上し、読みやすくなります。以下に、変数と型に関する基本的な情報を示します。
変数の宣言
編集変数を宣言する方法はいくつかありますが、基本的な方法は次の通りです:
let variableName: type;
ここで、variableName
は変数の名前であり、type
は変数の型です。
型推論
編集TypeScriptは型推論を行います。つまり、変数に初期値が与えられている場合、TypeScriptはその初期値から型を推論し、その型を変数に割り当てます。例:
let numberVariable = 10; // number型と推論される let stringVariable = "Hello"; // string型と推論される
明示的な型指定
編集型推論が十分ではない場合、または変数の型を明示的に指定したい場合は、型アノテーション(Type Annotation)を使用します:
let numberVariable: number = 10; let stringVariable: string = "Hello";
基本的な型
編集TypeScriptの基本的な型の一覧 型 説明 any 任意の型 number 数値 string 文字列 boolean 真理値 (true/false) void 値を返さない (undefined相当) null null undefined undefined never 発生しない値(例外が発生して関数が抜け出せなくなった場合など) unknown anyと同様任意の型、しかし、型の安全性を保持するための安全なany型 object オブジェクト array 配列 tuple 固定長の配列 enum 列挙型 union 複数の型のいずれか1つを受け入れ可能 intersection 複数の型を結合 type 型エイリアス。型に名前をつけた、再利用可能な定義 class クラス interface オブジェクトの構造や形状を定義 function 関数
注意: 上記の型は基本的なものであり、カスタム型を作成することも可能です。
any型
編集any
型は、任意の型を持つことを示します。すべての型のスーパーセットであり、型チェックが行われません。
let anyVariable: any = 10; anyVariable = "Hello"; // 問題なく代入できる
数値型(number)
編集TypeScriptの数値型はnumber
型です。これは整数や浮動小数点数などの数値を扱うことができます。
let integerValue: number = 10; // 整数値 let floatValue: number = 3.14; // 浮動小数点数
= 数値リテラル =
編集数値リテラルは、コード内で具体的な数値を表現するために使用されます。 これには、10進数、2進数、8進数、16進数などの表記方法があります。
let decimalLiteral: number = 10; // 10進数 let binaryLiteral: number = 0b1010; // 2進数(10進数の10と同じ) let octalLiteral: number = 0o12; // 8進数(10進数の10と同じ) let hexadecimalLiteral: number = 0xA; // 16進数(10進数の10と同じ)
これらの数値リテラルを使用することで、異なる進数の数値を直接コードに表現することができます。
TypeScriptのプログラムは、JavaScriptにトランスパイルされ、JavaScriptエンジンで実行されるので、同じく型numberも64ビット倍精度浮動小数点数です。
実行時に、Number.isInteger()
を使って動的に判定することは出来ますが、コンパイル時に整数に限定した型をアノテートすることは出来ません。
どうしても整数に限定した型がほしい場合は、BigInt オブジェクトがあります。
BigInt オブジェクトのリテラルは、123n
の様に数値に続けて n
を補います。
TSでの型名は bigint
です。
bigint と number の間では四則演算や比較演算は出来ません[1]。
Number() あるいは BigInt() で型を合わせてから演算する必要があります。
文字列型(string)
編集TypeScriptの文字列型は、JavaScriptの文字列型と同様に、文字のシーケンスを表すために使用されます。ただし、TypeScriptにはいくつかの追加機能があります。
まず、TypeScriptの文字列型には、バッククォートを使用したテンプレート文字列があります。これは、JavaScriptのバッククォートを使用したテンプレート文字列と似ていますが、TypeScriptでは、テンプレート文字列内に埋め込まれた式の型チェックを行うことができます。以下は、テンプレート文字列内に変数を埋め込んで使用する例です。
let name: string = "John"; let age: number = 30; let message: string = `My name is ${name} and I am ${age} years old.`; console.log(message); // "My name is John and I am 30 years old."
また、TypeScriptには、文字列リテラル型があります。これは、文字列リテラルを使用して型を定義することができる機能であり、文字列リテラル型を使用することで、文字列リテラルの集合に対して安全に操作を行うことができます。以下は、文字列リテラル型を使用して、有効な色名のみを受け付ける関数を定義する例です。
type Color = "red" | "green" | "blue"; function setColor(color: Color) { // ... } setColor("red"); // OK setColor("yellow"); // TypeScript error: Argument of type 'yellow' is not assignable to parameter of type 'Color'.
以上のように、TypeScriptの文字列型には、JavaScriptにはないいくつかの追加機能があります。これらの機能は、文字列処理をより簡単かつ正確に行うためのものであり、開発者にとって大きな利点を提供します。
真理値型(boolean)
編集TypeScriptの真理値型(boolean)は、JavaScriptの真理値型と同様に、trueまたはfalseの値を持ちます。
JavaScriptと同様に、TypeScriptの真理値型は、if文や三項演算子などの条件分岐に使用されます。しかし、TypeScriptの真理値型にはいくつかの差異があります。
まず、TypeScriptの真理値型は、明示的な型注釈を使用して変数に型を割り当てる場合に必要です。つまり、TypeScriptで変数に真理値型の値を代入する場合、明示的に型注釈を付ける必要があります。例えば、以下のコードを考えてみましょう。
let isDone: boolean = true;
このコードでは、変数isDoneにtrueという真理値型の値を代入しています。しかし、この代入文で明示的に型注釈を使用していることに注意してください。
また、TypeScriptの真理値型は、JavaScriptの厳密等価演算子(===)や不等価演算子(!==)の使用を推奨します。これは、TypeScriptが型安全性を重視しているためです。厳密等価演算子と不等価演算子は、値と型が一致しない場合にも比較を行うため、型の不一致によるバグを防ぐことができます。例えば、以下のコードを考えてみましょう。
let num: number = 0; let isTrue: boolean = false; if (num === isTrue) { console.log('This should not be executed!'); }
このコードでは、変数numに0という数値型の値を代入し、変数isTrueにfalseという真理値型の値を代入しています。その後、if文でnumとisTrueを比較しています。この比較は、JavaScriptではfalseになりますが、TypeScriptではコンパイルエラーとなります。これは、numとisTrueが型が異なるためです。このように、厳密等価演算子や不等価演算子の使用によって、TypeScriptは型安全性を保つことができます。
JavaScriptにおいても真理値型はありますが、明示的な型注釈の必要はありません。また、厳密等価演算子や不等価演算子の使用は推奨されますが、必須ではありません。JavaScriptは動的型付けを採用しており、このため比較演算子の使用によってバグを回避することはできません。
void
編集JavaScriptでは、void
キーワードは式を評価して何も返さないことを示します。たとえば、console.log()
関数は値を返さないため、型アノテーションとしてvoid
を使用することができます。
しかし、TypeScriptではvoid
はやや異なります。TypeScriptのvoid
型は、戻り値がない関数の戻り値の型を指定するために使用されます。具体的には、void
型は何も返さない関数(つまり、return
ステートメントを含まない関数)の戻り値の型を指定するために使用されます。void
型を指定することで、TypeScriptはその関数が値を返さないことを保証します。
例えば、以下のような関数があるとします。
function logMessage(message: string): void { console.log(message); }
この関数は、string
型の引数を受け取り、それをコンソールに出力しますが、何も返しません。そのため、void
型を戻り値の型として指定する必要があります。
一方、JavaScriptでは、void
は戻り値がないことを示すために使用されますが、型システムでは使用されません。JavaScriptでは、値を返さない関数は、単に何も返さないため、void
キーワードは必要ありません。
null
編集JavaScriptには、null
という値があります。この値は、変数に何も割り当てることができないことを示します。つまり、変数はnull
以外の値を持つことができますが、null
自体は値を持ちません。
一方、TypeScriptでは、null
型があります。null
型は、変数がnull
値またはundefined
値を持つことを明示的に示すために使用されます。
以下は、null
型の使用例です。
let myString: string | null = "Hello World"; myString = null;
この例では、myString
変数はstring
型またはnull
型を持つことができます。最初に、myString
変数に文字列を割り当て、次にnull
を割り当てることができます。string
型の変数にnull
を直接割り当てることはできません。
このように、TypeScriptのnull
型は、null
またはundefined
値を受け入れる変数の型を定義するために使用されます。JavaScriptでは、null
は値自体を表すために使用されますが、型として使用されることはありません。
undefined
編集JavaScriptには、undefined
という値があります。この値は、変数に値が割り当てられていないことを示します。つまり、変数はundefined
以外の値を持つことができますが、undefined
自体は値を持ちません。
一方、TypeScriptでは、undefined
型があります。undefined
型は、変数がundefined
値を持つことを明示的に示すために使用されます。
以下は、undefined
型の使用例です。
let myNumber: number | undefined; console.log(myNumber); // undefined
この例では、myNumber
変数はnumber
型またはundefined
型を持つことができます。変数に何も割り当てられていないため、console.log()
関数で変数の値を出力すると、undefined
が表示されます。
このように、TypeScriptのundefined
型は、変数がundefined
値を持つことを明示的に示すために使用されます。JavaScriptでは、undefined
は値自体を表すために使用されますが、型として使用されることはありません。
never
編集JavaScriptには、never
型はありません。しかし、TypeScriptにおいては、never
型は存在します。この型は、関数が終了しないことを示します。
例えば、次のような関数があります。
function throwError(message: string): never { throw new Error(message); }
この関数は、Error
オブジェクトをスローしています。Error
オブジェクトがスローされた場合、関数は終了することはありません。そのため、この関数の戻り値の型はnever
となっています。
また、never
型は、型システムの中でエラーを表すためにも使用されます。例えば、以下のような関数があった場合、
function handleError(): never { while (true) { // エラー処理 } }
この関数は、while
ループ内でエラーを処理するために、無限ループを行います。このため、この関数の戻り値の型はnever
になります。
never
型は、TypeScriptの静的型付けにおいて、関数が終了しないことや、エラーを表すことを明示的に示すために使用されます。JavaScriptでは、never
型は存在しないため、この型を使用することはできません。
unknown
編集unknown
型は、TypeScript 3.0から導入された型の一つで、JavaScriptには存在しない型です。この型は、ある値が何であるかを明確にはわからない場合に使用されます。
JavaScriptでは、どんな型の値でも代入可能なのに対し、TypeScriptでは型の厳密さが求められます。そのため、TypeScriptの静的型付けにおいて、未知の型を扱う場合にはunknown
型が用意されています。
unknown
型を使うと、変数の型が未知の場合、型チェックを通過するためには、先に型ガードを行う必要があります。以下は、unknown
型の変数を扱う例です。
let value: unknown = "Hello, TypeScript!"; // 文字列型かどうかを判定する if (typeof value === "string") { // valueはstring型に変更されるため、以下のように文字列メソッドを使用できる console.log(value.toLowerCase()); // "hello, typescript!" }
このように、unknown
型は、変数の型が不明な場合に使用されます。そのため、値に対する型チェックを実施する必要があります。unknown
型を使用することで、JavaScriptにはない型安全性を保つことができます。
object
編集object
型は、TypeScriptにおける型の一つで、JavaScriptにも存在する型です。ただし、TypeScriptのobject
型はJavaScriptのobject
型とは異なる点がいくつかあります。
JavaScriptのobject
型は、すべてのオブジェクトを表す汎用的な型であり、配列や関数も含まれます。一方、TypeScriptのobject
型は、非プリミティブ型であり、オブジェクトのプロパティとそのプロパティの型を定義するために使用されます。
以下は、object
型を使用してオブジェクトを定義する例です。
let person: object = { name: "John", age: 30 };
この例では、person
変数にobject
型のオブジェクトを代入しています。この場合、object
型は、オブジェクトのプロパティとそのプロパティの型を定義するために使用されています。
一方、JavaScriptでは、object
型は汎用的な型であるため、オブジェクトのプロパティやその型を指定することはできません。また、JavaScriptには型注釈がないため、変数の型を指定することもできません。
let person = { name: "John", age: 30 };
このように、TypeScriptのobject
型は、JavaScriptのobject
型とは異なる点があるため、注意が必要です。
array
編集TypeScriptの配列型は、JavaScriptの配列型と非常に似ていますが、いくつかの追加機能があります。
まず、TypeScriptの配列型には、要素の型を指定することができます。これは、JavaScriptの配列型にはない機能であり、配列内の要素の型が一致しない場合に、TypeScriptは型エラーを発生させます。以下は、number型の配列を定義する例です。
let numbers: number[] = [1, 2, 3];
また、TypeScriptの配列型には、ジェネリック型を使用して、任意の型の配列を表現することができます。以下は、ジェネリック型を使用して、文字列型の配列を定義する例です。
let names: Array<string> = ["John", "Jane", "Bob"];
さらに、TypeScriptの配列型には、読み取り専用の配列型があります。これは、配列の要素を変更できないことを保証する型であり、TypeScriptは、読み取り専用の配列型を使用して、配列を操作する関数やメソッドが要素を変更しないことを確認します。以下は、読み取り専用の配列型を使用して、配列の要素を変更できないことを保証する例です。
let numbers: readonly number[] = [1, 2, 3]; numbers[0] = 4; // TypeScript error: Index signature in type 'readonly number[]' only permits reading.
以上のように、TypeScriptの配列型には、JavaScriptにはないいくつかの追加機能があります。これらの機能は、配列操作をより安全かつ正確に行うためのものであり、開発者にとって大きな利点を提供します。
tuple
編集tuple
は、TypeScriptにおける型の一つで、複数の要素を含む配列を表すために使用されます。JavaScriptにはtuple
型は存在しないため、TypeScriptとJavaScriptとの間には差異があります。
以下は、tuple
を使用して数値と文字列を持つ配列を定義する例です。
let myTuple: [number, string] = [1, "hello"];
この例では、myTuple
変数に[number, string]
型の配列を代入しています。[number, string]
型は、最初の要素が数値型、2番目の要素が文字列型であることを定義しています。
let myArray = [1, "hello"];
一方、JavaScriptでは、配列の要素には制限がありません。任意の型の要素を持つことができます。
このように、TypeScriptのtuple
型は、JavaScriptの配列とは異なる点があります。TypeScriptのtuple
型は、要素の数と型が固定されており、型安全性を高めることができます。
enum
編集enum
は、TypeScriptにおける型の一つで、列挙型を表現するために使用されます。JavaScriptにはenum
型は存在しないため、TypeScriptとJavaScriptとの間には差異があります。
以下は、enum
を使用して曜日を表す列挙型を定義する例です。
enum DayOfWeek { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday }
この例では、DayOfWeek
という名前の列挙型を定義しています。enum
キーワードを使用し、{}
の中に列挙する値を記述しています。ここでは、Sunday
からSaturday
までの曜日を定義しています。
一方、JavaScriptでは、列挙型を表現する方法はありません。通常は、定数を使用することで同様の機能を実現します。
const DayOfWeek = { Sunday: 0, Monday: 1, Tuesday: 2, Wednesday: 3, Thursday: 4, Friday: 5, Saturday: 6 };
このように、TypeScriptのenum
型は、JavaScriptの定数とは異なる点があります。TypeScriptのenum
型は、列挙型を表現するために特別に設計されており、列挙型の使用をより簡単に、かつ型安全に実現することができます。
Union型
編集複数の型を持つことを示すために、Union型を使用することができます:
let numberOrString: number | string; numberOrString = 10; // 問題なく代入できる numberOrString = "Hello"; // 問題なく代入できる
intersection
編集JavaScriptには「intersection」という機能はありませんが、TypeScriptでは利用可能です。
「intersection」は、複数の型を結合することで新しい型を作成する方法です。結合された型は、それらのすべての型のメンバーを持ちます。
例えば、以下のように Person
と Serializable
という2つのインターフェースがあるとします。
interface Person { name: string; age: number; } interface Serializable { serialize(): string; }
これら2つのインターフェースを結合して、新しいインターフェース PersonSerializable
を作成することができます。
type PersonSerializable = Person & Serializable;
PersonSerializable
は、 Person
と Serializable
の両方のメンバーを持ちます。したがって、 name
、 age
、および serialize
の3つのメンバーがあります。
const person: PersonSerializable = { name: "John", age: 30, serialize() { return `${this.name}, ${this.age}`; } };
このように、 intersection
を使用することで、異なる型の機能を1つの型にまとめることができます。
type
編集複雑な型を再利用するために、キーワードtypeを用いて 型エイリアスを定義できます:
type Point = { x: number; y: number; }; let point: Point = { x: 10, y: 20 };
class
編集TypeScriptには、クラス型という型があります。これは、クラスのインスタンスを型として指定できるという点で、JavaScriptとは大きく異なります。 クラス型は、JavaScriptのクラスと同様に、オブジェクト指向プログラミングの概念をサポートします。クラス型を使用することで、型安全性を高め、コードの保守性を向上させることができます。
例えば、以下のようなクラスがあるとします。
class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } greet() { console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old.`); } }
このクラスを使用して、以下のようにインスタンスを生成することができます。
const person1 = new Person('Alice', 25); person1.greet(); // Output: Hello, my name is Alice, and I'm 25 years old.
TypeScriptでは、このクラスを型として扱うことができます。
const person2: Person = new Person('Bob', 30); person2.greet(); // Output: Hello, my name is Bob, and I'm 30 years old.
このように、クラス型を使用することで、変数にクラスのインスタンスを代入する際に、そのインスタンスが持つメンバー変数やメソッドにアクセスできるようになります。これにより、より安全で読みやすいコードを書くことができます。
interface
編集TypeScriptのinterface
は、JavaScriptには存在しない概念です。interface
は、オブジェクトの形状(shape)を定義するために使用されます。オブジェクトがinterface
で定義された形状に適合しているかどうかを確認するために使用することができます。
JavaScriptには、オブジェクトの形状を定義する方法がありません。そのため、JavaScriptでは、実際にオブジェクトを作成し、プロパティやメソッドが存在するかどうかをチェックする必要があります。
TypeScriptのinterface
は、この問題を解決するために導入されました。interface
を使用することで、オブジェクトの形状を明確に定義することができ、コードの読みやすさや保守性が向上します。
例えば、以下のようにinterface
を使用してオブジェクトの形状を定義することができます。
interface Person { name: string; age: number; address?: string; }
上記のコードでは、Person
というinterface
が定義されています。Person
はname
とage
という必須のプロパティと、オプションのaddress
プロパティを持つオブジェクトの形状を表します。
このinterface
を使用して、以下のようにオブジェクトを作成することができます。
const person: Person = { name: 'Alice', age: 25, };
上記のコードでは、Person
インターフェースを使用して、name
とage
プロパティを持つオブジェクトを作成しています。また、address
プロパティはオプションであるため、省略することができます。
interface
を使用することで、オブジェクトの形状を明確に定義し、プロパティやメソッドが存在するかどうかを簡単にチェックすることができます。
function
編集TypeScriptにおいて、関数も型付けされるため、関数型を宣言することができます。JavaScriptには関数型の宣言方法がありませんが、TypeScriptでは型アノテーションまたはインターフェースを使用して関数の型を定義できます。
function add(a: number, b: number): number { return a + b; }
この例では、add
という関数が定義されています。add
関数は2つの引数(a
とb
)を取り、それぞれがnumber
型であることをTypeScriptに示しています。また、戻り値もnumber
型であることを示しています。
TypeScriptでは、シグネチャーと呼ばれる機能を使って、関数の型をより詳細に定義することができます。シグネチャーは、関数の引数と戻り値の型のみを定義し、関数の本体は含まれません。
// シグネチャーを使った関数型の例 type AddFunction = (a: number, b: number) => number; const add: AddFunction = (a, b) => { return a + b; };
namespace
編集TypeScriptのnamespace
はJavaScriptにはない機能であり、TypeScriptの静的型付けシステムの一部です。namespace
を使用することで、グローバル名前空間を汚染することなく、関連する関数、変数、クラスをグループ化できます。
以下は、TypeScriptのnamespace
の例です。
namespace MyNamespace { export const message: string = "Hello, world!"; export function showMessage() { console.log(message); } export class Person { constructor(public name: string, public age: number) {} public sayHello() { console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`); } } }
このnamespace
は、MyNamespace
という名前の名前空間を作成し、message
という変数、showMessage
という関数、そしてPerson
というクラスを含んでいます。export
キーワードは、これらの要素を名前空間外からアクセス可能にするために使用されます。
以下は、上記のnamespace
をJavaScriptに変換した例です。
var MyNamespace; (function (MyNamespace) { MyNamespace.message = "Hello, world!"; function showMessage() { console.log(MyNamespace.message); } MyNamespace.showMessage = showMessage; class Person { constructor(name, age) { this.name = name; this.age = age; } sayHello() { console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`); } } MyNamespace.Person = Person; })(MyNamespace || (MyNamespace = {}));
JavaScriptには、namespace
という概念がないため、上記のコードは、IIFE(Immediately-Invoked Function Expression)というテクニックを使用して、名前空間をエミュレートしています。このテクニックは、名前空間内の変数や関数がグローバルスコープに漏れ出すことを防ぎ、名前の競合を回避するために有用です。また、namespace
内で定義された要素は、MyNamespace
オブジェクトのプロパティとしてアクセスできます。
関数
編集TypeScriptの関数は、JavaScriptの関数と同じように動作しますが、いくつかの差異があります。
まず、TypeScriptの関数には、パラメータに型を指定することができます。これにより、関数のパラメータが期待される型と異なる場合に、TypeScriptがコンパイル時にエラーを検出することができます。
また、TypeScriptの関数には、戻り値に型を指定することができます。これにより、関数が返す値の型を定義することができます。
さらに、TypeScriptの関数には、オプションのパラメータやデフォルト値を指定することができます。これらの機能は、JavaScriptでも利用できますが、TypeScriptでは型情報を合わせて指定することができます。
TypeScriptの関数には、ジェネリック型やアロー関数など、JavaScriptには存在しない機能もあります。これらの機能を使うことで、より柔軟な関数を定義することができます。
以上のように、TypeScriptの関数は、JavaScriptの関数と比べて、より型安全で、より柔軟な機能を持っています。
型アノテーションのある関数定義
編集以下の例では、パラメータと戻り値の型を明示的に指定しています。
function greet(name: string): string { return "Hello, " + name; }
同じ関数をJavaScriptで書く場合は、型アノテーションは不要です。
function greet(name) { return "Hello, " + name; }
ラムダ式の型アノテーション
編集以下の例では、アロー関数のパラメータと戻り値の型を明示的に指定しています。
const greet = (name: string): string => "Hello, " + name;
同じ関数をJavaScriptで書く場合は、型アノテーションは不要です。
const greet = (name) => "Hello, " + name;
型パラメータを使ったジェネリック関数
編集以下の例では、T
という型パラメータを使って、配列の中身を逆順にする関数を定義しています。
function reverse<T>(array: T[]): T[] { return array.reverse(); }
同じ関数をJavaScriptで書く場合は、型パラメータを使うことはできません。
// JavaScriptでは型パラメータを使えないため、以下のように書く必要がある。 function reverse(array) { return array.reverse(); }
定義と定義ファイル
編集TypeScriptでは、型定義を別のファイルに分離することができます。以下の例では、greet.d.ts
というファイルに、greet
関数の型定義を書いています。
declare function greet(name: string): string;
この型定義ファイルを使用することで、JavaScriptのファイルでもTypeScriptの関数の型情報を利用することができます。
// greet.d.tsの型定義を読み込むことで、greet関数の型情報を利用できる。 console.log(greet("Alice")); // "Hello, Alice"
JavaScriptでは、型定義ファイルを使用することはできません。
定義ファイルの入手
編集TypeScriptでサードパーティ製ライブラリーやES2015などの標準ライブラリの型定義ファイルを入手する方法は以下の通りです。
npm
を使って型定義ファイルをインストールするnpm
はNode.jsのパッケージマネージャーであり、多くの型定義ファイルがnpmのパッケージとして提供されています。例えば、jQueryの型定義ファイルは@types/jquery
というパッケージとして提供されています。インストール方法は以下の通りです。npm install --save-dev @types/jquery
- 同様に、ES2015などの標準ライブラリの型定義ファイルも、
@types
スコープ内に存在します。例えば、ES2015の型定義ファイルは@types/es6-shim
というパッケージとして提供されています。以下のコマンドでインストールできます。 npm install --save-dev @types/es6-shim
- 型定義ファイルを手動でダウンロードする
- もし、npmに型定義ファイルが存在しない場合や、npmを使用しない場合は、型定義ファイルを手動でダウンロードすることもできます。例えば、jQueryの型定義ファイルはDefinitelyTypedというGitHubリポジトリで管理されています。以下のコマンドでリポジトリをクローンして、
index.d.ts
を手動で取得できます。 git clone https://github.com/DefinitelyTyped/DefinitelyTyped.git cd DefinitelyTyped/types/jquery/ cat index.d.ts
- 同様に、ES2015などの標準ライブラリの型定義ファイルも、DefinitelyTypedリポジトリ内に存在します。
- もし、npmに型定義ファイルが存在しない場合や、npmを使用しない場合は、型定義ファイルを手動でダウンロードすることもできます。例えば、jQueryの型定義ファイルはDefinitelyTypedというGitHubリポジトリで管理されています。以下のコマンドでリポジトリをクローンして、
定義ファイルの生成
編集TypeScriptの定義ファイルの生成方法には、以下の2つの方法があります。
--declaration
フラグを使用する方法- TypeScriptのコンパイラには、
--declaration
フラグを使用して、定義ファイルを生成することができます。このフラグを指定すると、TypeScriptのコンパイラが、.ts
ファイルをコンパイルした際に、.d.ts
ファイルを同時に生成します。 - 以下は、
greeter.ts
というファイルからgreeter.js
とgreeter.d.ts
を生成する例です。 tsc --declaration greeter.ts
- 生成された
greeter.d.ts
ファイルは、以下のようになります。 declare function greeter(person: string): string;
- TypeScriptのコンパイラには、
dts-gen
ツールを使用する方法dts-gen
は、TypeScriptのコードから定義ファイルを生成するためのツールです。このツールを使用するには、まずdts-gen
をグローバルにインストールします。npm install -g dts-gen
- 次に、生成したい
.ts
ファイルを含むプロジェクトのルートディレクトリで、以下のコマンドを実行します。 dts-gen --name myLib --project .
--name
フラグで指定した名前のディレクトリが作成され、そのディレクトリに.d.ts
ファイルが生成されます。生成されたファイルは、/// <reference path="./typings/index.d.ts" />
のような参照ディレクティブを含んでいるため、必要に応じて修正する必要があります。
以上の方法は、TypeScriptのプロジェクトで定義ファイルを生成する方法です。一方、JavaScriptでは、定義ファイルを生成する標準的な方法はありません。しかし、JSDocを使用して、関数やオブジェクトの型情報をドキュメントとして記述することで、JavaScriptのコードにも型情報を付与することができます。
定義ファイルの書式
編集TypeScriptの定義ファイルの書式は、以下のようになっています。
declare function functionName(param1: type1, param2: type2): returnType;
このように、declare
キーワードを用いて、関数や変数の宣言を行います。また、関数の引数や戻り値には、型アノテーションを付与することができます。
例えば、以下はgreeter
関数の定義ファイルの例です。
declare function greeter(person: string): string;
一方、JavaScriptには、定義ファイルの書式は存在しません。代わりに、JSDocと呼ばれるドキュメントコメントを使用して、関数や変数の型情報を記述することができます。
以下は、JSDocを使用してgreeter
関数に型情報を付与する例です。
/** * @param {string} person - The name of the person. * @returns {string} - The greeting message. */ function greeter(person) { return "Hello, " + person; }
このように、@param
タグや@returns
タグを使用して、引数や戻り値の型情報を記述します。
ただし、JavaScriptのJSDocは、TypeScriptの定義ファイルのようにコンパイル時に静的な型チェックを行うわけではないため、注意が必要です。
TSDoc
編集TSDocは、TypeScriptのためのドキュメンテーションコメント記法です。TSDocは、JSDocと同じように、ソースコード内にドキュメンテーションコメントを記述するための標準的な形式を提供します。
TSDocは、TypeScriptコードにおいて、型情報やパラメーター、戻り値などの情報を含むコメントを書くことができます。これにより、開発者は、TypeScriptコードを使用する際に、より詳細な情報を把握することができます。
TSDocは、TypeScriptの型情報に基づいて、APIドキュメントを自動的に生成することができます。これにより、開発者は、手作業でドキュメンテーションを作成する手間を省くことができます。
TSDocは、TypeScriptの公式ドキュメンテーションにも使用されており、広く採用されています。
- 開発の開始(2012年10月):
- Microsoftによって、Anders Hejlsberg(C#の主要開発者でもあります)などのチームによって開発が開始されました。
- TypeScriptは、JavaScriptの開発をより大規模で保守可能なものにするための静的型付けなどの機能を提供しました。
- 公式リリース(2012年10月):
- TypeScript 0.8が公式にリリースされ、広く利用可能になりました。
- 開発者は、TypeScriptを使用してJavaScriptのコードを書くことができるようになり、コンパイラによってJavaScriptに変換されました。
- 型注釈とコンパイルオプションの拡充(2013年 - 2014年):
- TypeScript 1.0がリリースされ、型注釈やコンパイルオプションなど、言語の安定性と柔軟性が向上しました。
- ECMAScript 6(ES6)のサポート(2015年):
- TypeScript 1.5が、ECMAScript 6(またはECMAScript 2015)のサポートを追加しました。
- アロー関数、ジェネレータ、クラス、モジュールなど、新しいJavaScriptの機能をサポートしました。
- 型エイリアス、ジェネリクスの強化(2017年 - 2018年):
- TypeScript 2.0からは、型エイリアスや非nullableの型など、型の機能が拡充されました。
- TypeScript 2.3では、条件型やMapped Typesなど、ジェネリクスに関連する新機能が導入されました。
- 非同期関数のサポート(2017年 - 2018年):
- TypeScript 2.1からは、非同期関数のサポートが向上し、
async
およびawait
キーワードが導入されました。
- TypeScript 2.1からは、非同期関数のサポートが向上し、
- Strict Null Checking(2017年 - 2018年):
- TypeScript 2.0以降、strict null checkingが導入され、nullおよびundefinedの型安全性が向上しました。
- TypeScript 3.xシリーズ(2018年 - 現在):
- TypeScript 3.xシリーズでは、Tuple型、readonly修飾子、Unknown型、Optional Chaining、Nullish Coalescing Operatorなど、新しい機能が追加されました。
- ESNextの機能への追随(常時更新中):
- TypeScriptはECMAScriptの進化に追随し、新しいJavaScriptの機能をサポートしています。
クラス
編集JavaScriptにECMAScript2015(ES6)で導入された、キーワード class (クラス)を TypeScript もサポートしています。
- コード例
class Hello { name: string; constructor(name: string = "world") { this.name = name; } toString(): string { return `Hello ${this.name}`; } print(): void { console.log(String(this)); } } const hello: Hello = new Hello(); hello.print(); const hello2: Hello = new Hello("my friend"); hello2.print(); console.log( `typeof Hello === ${typeof Hello} Object.getOwnPropertyNames(hello) === ${Object.getOwnPropertyNames(hello)} ` );
- 表示結果
Hello world Hello my friend typeof Hello === function Object.getOwnPropertyNames(hello) === name
- クラスのメンバーには、型を伴った宣言が必要です。
少しまとまったサイズのクラス
編集Ruby#ユーザー定義クラスの都市間の大圏距離を求めるメソッドを追加した例を、TypeScriptに移植。
- ユーザー定義クラス
class GeoCoord { longitude: number; latitude: number; constructor(longitude: number = 0, latitude: number = 0) { this.longitude = longitude; this.latitude = latitude; } toString(): string { let ew = "東経"; let ns = "北緯"; let long = this.longitude; let lat = this.latitude; if (long < 0.0) { ew = "西経"; long = -long; } if (lat < 0.0) { ns = "南緯"; lat = -lat; } return `(${ew}: ${long}, ${ns}: ${lat})`; } distance(other: GeoCoord): number { const i = Math.PI / 180; const r = 6371.008; return ( Math.acos( Math.sin(this.latitude * i) * Math.sin(other.latitude * i) + Math.cos(this.latitude * i) * Math.cos(other.latitude * i) * Math.cos(this.longitude * i - other.longitude * i) ) * r ); } } const Sites: { [key: string]: GeoCoord } = { "東京駅": new GeoCoord(139.7673068, 35.6809591), "シドニー・オペラハウス": new GeoCoord(151.215278, -33.856778), "グリニッジ天文台": new GeoCoord(-0.0014, 51.4778), }; for (const prop in Sites) { console.log(`${prop}: ${Sites[prop]}`); } const keys: string[] = Object.keys(Sites); const len = keys.length; keys.forEach(function (x, i) { let y = keys[(i + 1) % len]; console.log(`${x} - ${y}: ${Sites[x].distance(Sites[y])} [km]`); });
- 実行結果
東京駅: (東経: 139.7673068, 北緯: 35.6809591) シドニー・オペラハウス: (東経: 151.215278, 南緯: 33.856778) グリニッジ天文台: (西経: 0.0014, 北緯: 51.4778) 東京駅 - シドニー・オペラハウス: 7823.269299386704 [km] シドニー・オペラハウス - グリニッジ天文台: 16987.2708377249 [km] グリニッジ天文台 - 東京駅: 9560.546566490015 [km]
TypeScriptの高度な機能
編集TypeScriptにあってJavaScriptにないキーワード キーワード 説明 type
型エイリアスを定義するために使用されます。 interface
オブジェクトの形状を定義するために使用されます。 declare
外部コードの型情報を宣言するためのキーワードで、型定義ファイル (.d.ts) で主に使われます。 abstract
抽象クラスや抽象メソッドを定義するために使用されます。 implements
インターフェイスを実装するクラスを定義するために使用されます。 enum
列挙型を定義するために使用されます。 namespace
名前空間を定義するために使用されます。 import
外部モジュールからエクスポートされた変数や関数をインポートするために使用されます。 export
モジュールから変数や関数をエクスポートするために使用されます。 as
型キャストを行うために使用されます。 readonly
変数やプロパティを読み取り専用にするために使用されます。 never
決して戻らないことを表すために使用されます。関数が例外を投げるか無限ループする場合などに使われます。 unknown
安全でない任意の値を表すために使用されます。 keyof
オブジェクトのキーを取得するために使用されます。 in
オブジェクトに指定されたプロパティがあるかどうかを判断するために使用されます。 infer
型変数を推論するために使用されます。 Exclude
一方の型にあるが他方の型にない型を取得するために使用されます。 Partial
オブジェクトの全てのプロパティをオプションにするために使用されます。 Required
オブジェクトの全てのプロパティを必須にするために使用されます。 Pick
オブジェクトから指定したプロパティのみを取得するために使用されます。 Record
キーと値のペアを持つオブジェクトを作成するために使用されます。 Omit
オブジェクトから指定したプロパティを削除するために使用されます。 ReturnType
関数の戻り値の型を取得するために使用されます。
モジュールと名前空間
編集// myModule.ts export const myFunction = () => { console.log('Hello from myFunction!'); } export interface MyInterface { name: string; age: number; } // main.ts import { myFunction, MyInterface } from './myModule'; myFunction(); const myObject: MyInterface = { name: 'John', age: 30, };
このコードは、TypeScriptにおけるモジュールの使い方の例です。
JavaScriptでもES6からは、export
キーワードやimport
キーワードを使用してモジュールを定義することができるようになりました。しかし、TypeScriptでは、これらのキーワードに加えて、型定義も一緒に書くことができます。
myModule.ts
では、export
キーワードを使用してmyFunction
とMyInterface
を外部に公開しています。myFunction
は単純な関数で、console.log
でメッセージを表示します。MyInterface
は、name
とage
という2つのプロパティを持つインターフェースです。
main.ts
では、myModule.ts
からmyFunction
とMyInterface
をimport
しています。そして、myFunction
を呼び出し、MyInterface
を使ってオブジェクトを作成しています。
TypeScriptのモジュール機能は、JavaScriptのものとほぼ同じですが、型定義を含めることで、コードの安全性と保守性を向上させることができます。
アノテーションとデコレータ
編集class MyClass { @myDecorator public myProperty: string; constructor(@myParameterDecorator private _myParameter: number) { this.myProperty = 'Hello, world!'; } @myMethodDecorator public myMethod(): void { console.log(this._myParameter); } } function myDecorator(target: any, key: string) { // do something with target and key } function myMethodDecorator(target: any, key: string, descriptor: PropertyDescriptor) { // do something with target, key, and descriptor } function myParameterDecorator(target: any, key: string, index: number) { // do something with target, key, and index }
このコードは、TypeScriptにおけるアノテーションとデコレータの使い方の例です。
デコレータは、クラスのプロパティやメソッド、パラメーターに対して、実行時にカスタム処理を追加するための構文です。アノテーションは、特定の型や属性を指定するための構文です。
このコードでは、@myDecorator
、@myMethodDecorator
、@myParameterDecorator
という3つのデコレータを使用しています。@myDecorator
は、MyClass
クラスのmyProperty
プロパティに適用され、@myMethodDecorator
はmyMethod
メソッドに適用されています。@myParameterDecorator
は、MyClass
クラスの_myParameter
パラメーターに適用されています。
それぞれのデコレータ関数内では、引数として与えられたtarget
、key
、descriptor
、index
というオブジェクトにアクセスして、必要な処理を行います。たとえば、@myDecorator
デコレータ内では、target
とkey
を使って、MyClass
クラスのmyProperty
プロパティに対するカスタム処理を追加することができます。
また、MyClass
のコンストラクターでは、@myParameterDecorator
を使用して、_myParameter
パラメーターに対するカスタム処理を追加しています。これにより、コンストラクターが実行される前に、パラメーターに対するチェックや変換を行うことができます。
JavaScriptにおいては、デコレータに対応する構文はありませんが、TypeScriptのデコレータはJavaScriptのプロトタイプチェーンを利用して実現されています。また、アノテーションに相当するものもありません。
オプショナルなプロパティとreadonly修飾子
編集interface MyInterface { readonly id: number; name: string; age?: number; } const myObject: MyInterface = { id: 1, name: 'John', }; // Cannot assign to 'id' because it is a read-only property. // myObject.id = 2; myObject.age = 30;
このコードは、TypeScriptでのインターフェースとオブジェクトの使用例を示しています。
MyInterface
インターフェースは、id
、name
、age
の3つのプロパティを定義しています。id
プロパティはnumber
型で読み取り専用 (readonly
) です。name
プロパティはstring
型です。age
プロパティはnumber
型で、省略可能なオプションプロパティです。
myObject
定数は、MyInterface
インターフェースに準拠したオブジェクトです。id
、name
プロパティは指定されており、age
プロパティは省略されています。id
プロパティは読み取り専用なので、オブジェクトが作成された後に再代入することはできません。
- コメントされた行
myObject.id = 2;
は、id
プロパティが読み取り専用であるため、再代入しようとするとエラーが発生します。 myObject.age = 30;
のように、オブジェクトのプロパティage
に値を追加することはできます。age
はオプションプロパティであるため、省略することもできますが、存在する場合はnumber
型である必要があります。
これにより、TypeScriptでは型安全性が向上し、コードの信頼性と保守性が向上します。
列挙型
編集enum Color { Red, Green, Blue, } function printColor(color: Color) : void { switch (color) { case Color.Red: console.log('Red'); break; case Color.Green: console.log('Green'); break; case Color.Blue: console.log('Blue'); break; // default 節がないのは網羅性を担保するため } } printColor(Color.Red);
このコードは、列挙型(enum)を使用して、TypeScriptで列挙値を扱う方法を示しています。
列挙型は、固定値の集合を表すための型であり、定数のように扱えます。TypeScriptにおける列挙型は、JavaScriptのオブジェクトに近いものですが、列挙型には定数に対して名前を与えることができます。
この例では、Color
という名前の列挙型を定義し、3つの値(Red
、Green
、Blue
)を持たせています。関数printColor
は、Color
型の引数を受け取り、その値が何であるかに応じて、対応する色の文字列をコンソールに出力します。
このコードには default
節がないため、すべての enum Color
のメンバーに対して switch
文での処理が定義されていないと、TypeScript コンパイラがエラーを出力します。つまり、 enum Color
に新しいメンバーを追加するときは、すべての場合分けを考慮して switch
文を更新する必要があります。
そのため、このコードでは網羅性が担保されており、メンバーの追加などの変更があった場合にもコンパイルエラーで指摘されるので、開発者が見落としを防ぐことができます。
JavaScriptには列挙型の概念がないため、同じような機能を実現するためには、オブジェクトや配列を使用することが一般的です。例えば、以下のようなconst
オブジェクトを定義して列挙型の代わりに使用することができます。
const Color = { Red: 0, Green: 1, Blue: 2, }; const myColor = Color.Red; function printColor(color) { switch (myColor) { case Color.Red: console.log('Red'); break; case Color.Green: console.log('Green'); break; case Color.Blue: console.log('Blue'); break; } } printColor(Color.Red);
このようなオブジェクトと比べてTypeScriptの列挙型が優れている点としては、エディターの自動補完機能や、スイッチ文による型安全の検査が利用でき、開発効率の向上に役立つことが挙げられます。
型の合成
編集type MyType = { name: string; age: number; }; type MyOptionalType = Partial<MyType>; type MyReadOnlyType = Readonly<MyType>; type MyCompositeType = MyType & MyOptionalType & MyReadOnlyType; const myObject: MyCompositeType = { name: 'John', age: 30, };
このコードは、TypeScriptにおける型の合成の例です。
まず、MyType
という名前の型を定義しています。この型は、name
とage
という2つのプロパティを持つオブジェクト型です。
次に、MyOptionalType
という名前の型を定義しています。この型は、MyType
型のすべてのプロパティをオプションにしたもので、Partial
というジェネリック型を使用して定義されています。つまり、MyOptionalType
型を持つオブジェクトは、name
とage
の両方、あるいはいずれかのプロパティを持っているかもしれないということです。
その後、MyReadOnlyType
という名前の型を定義しています。この型は、MyType
型のすべてのプロパティを読み取り専用にしたもので、Readonly
というジェネリック型を使用して定義されています。つまり、MyReadOnlyType
型を持つオブジェクトのプロパティは、読み取り専用であるため、値を変更することはできません。
最後に、MyCompositeType
という名前の型を定義しています。この型は、MyType
型とMyOptionalType
型とMyReadOnlyType
型をすべて組み合わせたものです。つまり、MyCompositeType
型を持つオブジェクトは、name
とage
の両方、あるいはいずれかのプロパティを持っているかもしれず、すべてのプロパティが読み取り専用であるということです。
最後に、myObject
という名前のオブジェクトを定義し、MyCompositeType
型で型注釈しています。myObject
は、name
が'John'
で、age
が30
であるオブジェクトであり、すべてのプロパティが読み取り専用であるため、プロパティの値を変更することはできません。
JavaScriptには、型の合成の概念はありません。しかし、JavaScriptでは、複数のオブジェクトを結合して新しいオブジェクトを作成するために、オブジェクトのスプレッド演算子(...
)が使用できます。例えば、以下のようにして、Object.assign()
メソッドを使用して、複数のオブジェクトを結合することができます。
const myObject = Object.assign({}, obj1, obj2, obj3);
TypeScriptの開発環境
編集TypeScriptには様々な開発環境がありますが、以下に代表的な方法を示します。
TypeScriptのIDE
編集TypeScriptのIDEとしては、以下のようなものがあります。
- Emacs
- Vim
- Visual Studio Code
- WebStorm
- Atom
- Sublime Text 3
これらのIDEはTypeScriptの構文ハイライトや、型チェック、自動補完などの機能を備えています。特にVisual Studio CodeはTypeScriptの開発に最適化されており、TypeScriptのデバッグ機能も利用可能です。
デバッグ方法
編集TypeScriptをデバッグする場合、以下のような方法があります。
- ブラウザのデベロッパーツールを利用する(主にフロントエンド開発)
- VSCodeのデバッグ機能を利用する(主にバックエンド開発)
フロントエンド開発においては、ブラウザの開発者ツールを利用することが一般的です。開発者ツールを起動し、ブレークポイントを設定することで、TypeScriptのコードをステップ実行することができます。
バックエンド開発においては、Visual Studio Codeのデバッグ機能を利用することが一般的です。VSCodeでプロジェクトを開き、ブレークポイントを設定した上で、デバッグモードで起動することで、TypeScriptのコードをデバッグすることができます。
テスト方法
編集TypeScriptでのテスト方法としては、以下のような方法があります。
- Jest
- Mocha + Chai
- AVA
これらのツールは、TypeScriptのテストをサポートしています。Jestは特にTypeScriptとの親和性が高く、設定の手間が少ないことが特徴です。
テストを書く際は、describeやit、expectなどのAPIを使い、テストスクリプトを記述します。テストコードは通常、TypeScriptで書かれるため、テストランナーによってTypeScriptからJavaScriptへのコンパイルが自動的に行われます。
以上が、TypeScriptの開発環境、デバッグ方法、テスト方法についての簡単な紹介です。
TypeScriptとフロントエンド・フレームワーク
編集TypeScriptを活用したフロントエンド開発には、様々なフレームワークが存在し、それぞれの特性によってプロジェクトの選択が可能です。特に、仮想DOMに基づくフレームワークや仮想DOMを使わないアプローチがあり、それぞれ一長一短があります。
仮想DOM
編集仮想DOM(Virtual DOM)は、ウェブアプリケーションの効率的なUI更新を実現するための概念です。通常、ウェブブラウザ上でのUIの変更はDOM(Document Object Model)と呼ばれる階層構造を持つ要素を直接変更することで行われます。しかし、これには効率上の課題が存在します。
仮想DOMは、UIの変更をより効率的に行うために導入された手法で、以下のような仕組みを採用しています:
- 仮想DOMの構築
- 変更前のUI状態と変更後のUI状態の差分を把握するために、現在のDOMツリーの簡易的なコピーである仮想DOMが構築されます。
- 変更の検出
- 変更前と変更後の仮想DOMを比較して、変更がある部分を検出します。この過程で効率的に変更点を見つけることができます。
- 差分の適用
- 変更点が特定されたら、それに基づいて実際のDOMツリーに対する変更が行われます。ただし、これは必要な最小限の変更のみが行われるため、全体の再描画が行われるよりも効率的です。
- 再描画
- 実際のDOMへの変更が行われた後、ブラウザが必要な部分だけを再描画します。この際、再描画対象が限定されるため、全体のページの再描画よりも高速です。
仮想DOMに基づくフレームワーク
編集React.js + TypeScript: 効率的なUI構築と型安全性の融合
編集React.jsはMeta(旧Facebook)によって開発されたUI構築のためのライブラリであり、TypeScriptとの組み合わせにより型安全性が向上します。仮想DOMを利用することで、効率的で柔軟なUI更新が可能であり、大規模なアプリケーションにも適しています。
主な特徴:
- 型安全性の向上
- TypeScriptとの統合により、静的な型チェックが可能となり、開発者はコードの安全性を確保できます。型の恩恵を受けながら開発を進めることができます。
- 仮想DOMによる効率的なUI更新
- 仮想DOMを活用することで、変更がある部分のみを効率的に更新することができます。これにより、UIの更新プロセスが迅速であり、ユーザーエクスペリエンスが向上します。
- 大規模なアプリケーションに適したアーキテクチャ
- React.jsはコンポーネントベースのアーキテクチャを採用しており、これにより大規模なアプリケーションの構築や管理が容易になります。
React.js + TypeScriptの組み合わせは、型安全性と効率的なUI更新の両方を追求する現代のフロントエンド開発において非常に重要な選択肢となっています。
Angular: 大規模で型安全なアプリケーション構築
編集AngularはGoogleによって開発され、大規模で複雑なアプリケーションの構築に適しています。元々TypeScriptで記述されており、強力な型システムを活かした開発が可能です。
主な特徴:
- 大規模アプリケーション向けのアーキテクチャ
- Angularはモジュール、サービス、ディレクティブ、コンポーネントなどのコンセプトを採用しており、これらを組み合わせて大規模なアプリケーションを構築しやすくしています。
- 強力な型システム
- TypeScriptを採用しているため、開発者は静的な型付けを利用してコードの安全性を確保できます。これにより、エディタの補完機能やエラーチェックが強化されます。
- 豊富な機能セット
- Angularはルーティング、フォーム処理、HTTPクライアントなど、標準で豊富な機能を提供しており、これにより開発者はアプリケーションの開発において効率的に作業できます。
Angularはこれらの特徴を組み合わせ、大規模で複雑なアプリケーションの開発をサポートしています。型安全性と豊富な機能セットにより、堅牢でメンテナンス性の高いアプリケーションを構築することが可能です。
Vue.js + TypeScript: 柔軟性と型安全性の調和
編集Vue.jsはEvan Youによって開発され、シンプルで柔軟な構文を提供するライブラリです。Vue.jsにTypeScriptを組み込むことで、プロジェクトの可読性や保守性が向上します。
主な特徴:
- 柔軟でシンプルな構文
- Vue.jsは直感的で柔軟な構文を提供し、コンポーネント指向のアーキテクチャに基づいています。これにより、初心者から経験豊富な開発者までが手軽に利用できます。
- TypeScript統合による型安全性
- TypeScriptを導入することで、開発者は静的な型付けによる安全なコーディングを享受できます。タイプミスやエラーを早期に検知し、可読性の高いコードを実現します。
- 単一ファイルコンポーネント
- Vue.jsではHTML、JavaScript、CSSを1つのファイルにまとめた単一ファイルコンポーネントが使われます。これにより、コンポーネントごとに必要なものが一元管理され、開発プロセスがスムーズになります。
Vue.js + TypeScriptの組み合わせは、柔軟性と型安全性のベストなバランスを提供します。豊富なエコシステムと優れたドキュメントにより、開発者は生産性を維持しながら高品質なアプリケーションを構築できます。
仮想DOMを使わないフレームワーク
編集Svelte: 仮想DOMを使わない新しいアプローチ
編集Svelteは仮想DOMを使わず、ビルド時にコンパイルされる手法を採用しています。この特異なアプローチにより、ランタイムのオーバーヘッドが極めて少なく、最終的なアプリケーションのサイズを著しく小さく保つことができます。
主な特徴:
- ランタイム効率
- コンポーネントはビルド時にフレームワークのコードに変換され、ランタイムには最適化されたバニラJavaScriptが残ります。これにより、実行時のオーバーヘッドが最小限に抑えられます。
- サイズの最適化
- Svelteは変更の必要な部分だけをコンパイルし、不要なライブラリやフレームワークの追加を排除します。結果として、最終的なアプリケーションのサイズが非常に小さくなります。
- TypeScript統合
- SvelteはTypeScriptとも統合が可能で、開発者は型安全性を確保しつつ、効率的でコンパクトなアプリケーションを構築できます。
このアプローチにより、Svelteは現代のフロントエンド開発において、軽量で効率的な選択肢として注目を浴びています。
Alpine.js: 軽量かつ直感的なUI制御
編集Alpine.jsはJavaScriptとHTMLの標準的な属性を利用し、UIを制御するための軽量なライブラリです。仮想DOMを使用せず、直感的でシンプルな構文を提供しています。
主な特徴:
- 軽量性
- Alpine.jsは小さなサイズであり、他の大規模なフレームワークやライブラリを必要としません。これにより、アプリケーションのファイルサイズを最小限に抑えます。
- 直感的な構文
- HTMLの標準的な属性を使用してUIを制御するため、開発者は追加の学習コストなしに簡潔で直感的なコードを書くことができます。
- シンプルなプロジェクトに適している
- 仮想DOMを使用せず、コンパクトな構文がアプリケーションのシンプルなプロジェクトに適しています。冗長なコードや複雑な構造を排除し、開発プロセスを簡素化します。
Alpine.jsはこれらの特性により、小規模なプロジェクトや簡潔なUIの実装において優れた選択肢となっています。
Hyperapp: 軽量で効率的な仮想DOMを使わないフレームワーク
編集Hyperappは小規模なフレームワークで、特筆すべきは仮想DOMを使用しない点です。基本的な機能を提供しながらも、軽量で効率的なアプリケーションの開発が可能です。
主な特徴:
- 軽量性
- Hyperappは小さなサイズであり、必要な機能に焦点を当てています。これにより、アプリケーションのファイルサイズを最小限に抑えます。
- 効率的なアプリケーション
- 仮想DOMを使わないことで、ランタイムのオーバーヘッドを削減し、アプリケーションのパフォーマンスを向上させます。簡潔なコードで効率的なアプリケーションを構築できます。
- 自由度の向上
- 仮想DOMを使わないアプローチにより、開発者は直接DOMに対して操作を行うことができ、アプリケーションの挙動を完全に制御できます。
これらの特徴により、Hyperappは小規模なプロジェクトや軽量なアプリケーションの開発に適しています。仮想DOMを使わないことで得られる自由度は、柔軟で効率的なフロントエンド開発に貢献しています。
仮想DOMでないとコンポーネント化は出来ないのか? =
編集仮想DOMを使用しない場合でもコンポーネント化は可能です。コンポーネント化は、UIや機能を独立した部品として分割し、再利用性や保守性を向上させる開発手法です。仮想DOMはこの手法を実現するための一つのアプローチですが、他のアプローチも存在します。
例えば、Vue.jsは仮想DOMを採用していますが、Svelteは仮想DOMを使わずにコンポーネント化を実現しています。Svelteでは、ビルド時にコンポーネントがフレームワークによって最適化され、効率的なランタイムが生成されます。
コンポーネント化は、仮想DOMがなくても、適切なツールやライブラリを使用することで実現可能です。 ただし、仮想DOMを使用しない場合、手動でDOMの変更や再描画を管理する必要があり、その分コードの複雑性が増す可能性があります。選択はプロジェクトの要件や開発者の好みに依存するため、慎重に検討することが重要です。
その他のフロントエンドフレームワーク
編集他にも様々なフロントエンドフレームワークが存在します。以下はいくつかの注目すべきフレームワークです。
- Next.js
- React.jsベースのフレームワークで、サーバーサイドレンダリングや静的サイト生成などの機能を提供しています。React.jsの強力な機能に加え、SEO向上やパフォーマンス最適化が期待できます。
- Nuxt.js
- Vue.jsベースのフレームワークで、Next.jsにインスパイアされています。Vueのシンプルな構文と、サーバーサイドレンダリング、ルーティングの自動生成などを組み合わせています。
- Gatsby
- React.jsを利用した静的サイトジェネレーターで、高速かつSEOフレンドリーなウェブサイトを構築できます。GraphQLを使用してデータのクエリが可能です。
- Sapper (SvelteKit)
- Svelteフレームワークの一部で、サーバーサイドレンダリングやルーティングの機能を提供します。SvelteKitとして進化しています。
- Quasar Framework
- Vue.jsベースのフレームワークで、モバイルアプリケーション、デスクトップアプリケーション、ウェブアプリケーションなど、異なるプラットフォーム向けのアプリケーションを効率的に構築できます。
これらのフレームワークはそれぞれ特有の特徴や用途に合わせて設計されており、プロジェクトのニーズや開発者の好みによって選択されることがあります。選択肢を検討する際には、ドキュメンテーションやコミュニティの活発さ、過去の実績なども考慮すると良いでしょう。
TypeScriptとバックエンド開発
編集TypeScriptは、JavaScriptのスーパーセットであり、静的型付けをサポートするプログラミング言語です。TypeScriptは主にフロントエンド開発において人気がありますが、バックエンド開発にも利用されています。以下は、TypeScriptを使用したバックエンド開発においてExpress、およびNestJSとの関連性についての簡単な説明です。
TypeScriptとExpress
編集ExpressはNode.jsのWebアプリケーションフレームワークであり、シンプルで柔軟な構造を提供します。ExpressアプリケーションをTypeScriptで書くことで、より安全かつ保守しやすいコードを実現できます。
// TypeScriptで書かれたExpressの例 import express from 'express'; const app = express(); const port = 3000; app.get('/', (req, res) => { res.send('Hello, Express with TypeScript!'); }); app.listen(port, () => { console.log(`Server is running on http://localhost:${port}`); });
TypeScriptとNestJS
編集NestJSは、エレガントで効果的なNode.jsフレームワークであり、Angularの影響を受けています。NestJSは、TypeScriptをサポートし、デコレータやモジュールのシステムを使用して、コードの構造を明確にし、開発を容易にします。
// TypeScriptで書かれたNestJSの例 import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); } bootstrap();
これらの例は、TypeScriptを使用してExpress、およびNestJSでバックエンドを開発するための基本的な構造を示しています。各フレームワークやライブラリは異なる特性を持っていますが、TypeScriptを組み合わせることで、コードの品質や保守性を向上させることができます。
TypeScriptの実践的な活用例
編集実践的なサンプルコード
編集TypeScriptでの開発のメリットとデメリット
編集まとめ
編集TypeScriptのまとめ
編集次に学ぶべきこと
編集
.d.ts
編集.d.ts
ファイルは、TypeScriptで型定義ファイルと呼ばれるものです。
型定義ファイルには、JavaScriptライブラリやフレームワークなどの外部コードの型情報が記述されています。TypeScriptコンパイラはこの型情報を使って、外部コードを型チェックしたり、自動補完の情報を提供したりします。
.d.ts
ファイルの中身は、通常以下のようなものが含まれます。
declare
キーワードで外部モジュールやグローバル変数などを宣言- インターフェイスやクラス、型エイリアスなどの型定義
- 関数シグネチャ
- モジュールやネームスペースの宣言
例えば、Node.jsのfs
モジュールの型定義ファイル(node_modules/@types/node/fs.d.ts
)には以下のようなコードが書かれています。
declare module "fs" { import * as stream from "stream"; export * from "fs/promises"; export const symlink: (...args: any[]) => any; // ... 他の関数やインターフェイスの型定義が続く }
型定義ファイルは、通常外部ライブラリに同梱されていたり、@types
名前空間にパッケージとして公開されています。型定義が存在しない場合は、自分で作成する必要があります。
.d.ts
ファイルは、手動で作成することもできますが、もしライブラリがnpmパッケージである場合、@types
スコープに公開されている別のパッケージから、簡単に取得することができます。例えば、lodash
ライブラリをTypeScriptで使用する際には、@types/lodash
パッケージをインストールすることによって、.d.ts
ファイルを手動で作成する代わりに、TypeScriptが自動的に型情報を認識できます。
このように、.d.ts
ファイルはTypeScriptプロジェクトで外部コードを型安全に扱うために必須の存在となっています。
制御構造
編集TypeScriptは、JavaScriptと同じ制御構造の構文を持ちます。
条件文
編集- if文の例
let num: number = 0.0 / 0.0 if (num < 0.0) { console.log('負') } else if (num > 0.0) { console.log('正') } else if (num == 0.0){ console.log('零') } else { console.log('NaN') }
- 実行結果
NaN
- switch文の例
let num: number = 0.0 / 0.0; switch (true) { case num < 0.0: console.log("負") break case num > 0.0: console.log("正") break case num == 0.0: console.log("零") break default : console.log("NaN") }
- 実行結果
NaN
反復文
編集型アノテート以外は、JavaScriptと違いはありません。
- while文
let i: number = 0; while (i < 10) { console.log(i); i++; }
- do-while文
let i: number = 0; do { console.log(i); i++; } while (i < 10);
- for文
for (let i: number = 0; i < 10; i++) { console.log(i); }
- for-in文
const obj: object = { x: 2, y: 3, z: 5 }; for (const prop in obj) { console.log(`${prop}: ${obj[prop]}`); } // x: 2 // y: 3 // z: 5
- for-of文
const ary: string[] = [..."XYZ"]; for (const el of ary) { console.log(el); } // X // Y // Z
スプライス構文 [..."XYZ"]
を使うには
npx tsc --target es2015
とES2015以降をターゲットに指定する必要がありました。
ES2015以前で、スプライス構文相当のことを行うには
[..."XYZ"]
を
"XYZ".split("")
に置換えます。これで、同じ
['X', 'Y', 'Z']
となります。
スプライス構文は、他にも引数リストなどでも使えるので、この方法が全てのスプライス構文の置換えにはなりません。
ちなみに Babal は、
const ary: string[] = [..."XYZ"];
から
"use strict"; function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } var ary = _toConsumableArray("XYZ");
を生成します。
tsconfig.json
編集tsc は、tsconfig.json で動作を変える事ができます。
最初は
npx tsc --init
とすると雛形が生成されます。
設定項目(一部):
noImplicitAny
(boolean): 暗黙的にany
と推論された場合は即座にエラーにする。strictNullChecks
(boolean):undefined
やnull
の可能性がある式に対して操作を試みることを禁止する。strictFunctionTypes
(boolean): 関数の引数の型、型変数を持つクラスの型引数に対するチェックが厳しくなる。strictBindCallApply
(boolean):Function
及びそのサブタイプに対するbind
、call
、apply
の呼び出しが厳格化される。strictPropertyInitialization
(boolean): 定義時にもコンストラクタ内でも初期化されないクラスのインスタンス変数をエラーにする。noImplicitThis
(boolean):this
がany
と推論される場合はエラーにする。strict
(boolean):noImplicitAny
、strictNullChecks
、strictFunctionTypes
、strictBindCallApply
、strictPropertyInitialization
、noImplicitThis
を全て有効化する。
附録
編集TypeScript チートシート
編集// 変数宣言 // @var x - 変数 // @type {number} let x = 5; x = 6; // OK // 定数宣言 // @var y - 定数 // @type {number} const y = 5; // y = 6; // エラー: y は読み取り専用 // 型アノテーション付き変数宣言 // @var z - 変数 // @type {number} let z: number = 5; // 関数 // @param x - 引数 // @param x.type {number} // @returns - 戻り値 // @returns.type {number} function f1(x: number): number { return x * x; } // 関数 (引数の型アノテーションがない) // @param x - 引数 // @returns - 戻り値 // @returns.type {void} function f2(x) { console.log(x); } // OK: 引数 x の型は any 型と推論される // any型付きの関数 // @param x - 引数 // @param x.type {any} // @returns - 戻り値 // @returns.type {void} function f3(x: any): void { console.log(x); } // OK // 型エイリアス // @typedef {number} T type T = number; // 型エイリアスを使った変数宣言 // @var u - 変数 // @var u.type {T} = number let u: T; // ジェネリック関数 // @template T // @param items - 配列 // @param items.type {T[]} // @returns - 配列 // @returns.type {T[]} function gf<T>(items: T[]): T[] {} // ユニオン型 // @typedef {U} // @type {number | string} type U = number | string; // 交差型 // @typedef {Q} // @type {number & string} type Q = number & string; // 注: number & string は never 型 // タプル型 // @typedef {R} // @type {[string, number]} type R = [string, number]; // 配列リテラル [1, 2, 3]; // 構造化代入 // @var x1 - 変数 // @var x1.type {number} // @var y1 - 変数 // @var y1.type {number} // @var z1 - 変数 // @var z1.type {number} const [x1, y1, z1] = [1, 2, 3]; // const x2, y2, z2 = [1, 2, 3]; // エラー: 左辺値がプロパティアクセスでなくデフォルト値がない // 配列への添字アクセス // @var ary - 配列 // @type {number[]} const ary = [2, 3, 5, 7, 11]; ary[3]; // 添字アクセス // 制御構文 // if文 // @param 条件式 - 条件式 // @param 条件式.type {boolean} // @returns - 戻り値 // @returns.type {void} if (条件式) { /* 文真 */ } else { /* 文偽 */ } // if文 (elseなし) // @param 条件式 - 条件式 // @param 条件式.type {boolean} // @returns - 戻り値 // @returns.type {void} if (条件式) { /* 文真 */ } // switch文 // @param 式 - 式 // @param 式.type {any} // @returns - 戻り値 // @returns.type {void} switch (式) { case 式1: /* 文1 */ break; // ... case 式n: /* 文n */ break; default: /* 文d */ } // while文 // @var x - 変数 // @var x.type {number} let x = 0; while (x < 5) { console.log(x); x++; } // for文 // @var i - 変数 // @var i.type {number} for (let i: number = 0; i < 10; i++) { console.log(i); } // オブジェクト指向 // クラス // @class C class C { // メンバー変数1 // @var member1 - メンバー変数 // @var member1.type {number} member1: number; // メンバー変数2 // @var member2 - メンバー変数 // @var member2.type {number} member2: number; // コンストラクタ // @constructor // @param {...any} 仮引数リスト - 引数 constructor(仮引数リスト) { /* ... */ } // メソッド1 // @returns - 戻り値 // @returns.type {void} method1() { /* ... */ } // メソッド2 // @returns - 戻り値 // @returns.type {void} method2() { /* ... */ } } // インスタンス化 // @var obj - オブジェクト // @var obj.type {C} const obj: C = new C(実引数リスト);
コードギャラリー
編集エラトステネスの篩
編集/** * エラトステネスの篩を用いて、n以下の素数を全て出力する関数 * @param {number} n 素数を探す範囲の上限値 * @returns {number[]} - 素数の配列 */ function eratosthenes(n: number): number[] { const sieve: boolean[] = new Array(n + 1).fill(true); sieve[0] = false; sieve[1] = false; const result: number[] = []; for (let i = 2, len = sieve.length; i * i < len; i++) { if (sieve[i]) { for (let j = i * i; j < len; j += i) { sieve[j] = false; } } } for (let i = 2, len = sieve.length; i < len; i++) { if (sieve[i]) { result.push(i); } } return result; } // Example usage console.log(eratosthenes(100));
最大公約数と最小公倍数
編集/** * 2つの整数の最大公約数を再帰的に求める関数 * @param {number} m - 整数1 * @param {number} n - 整数2 * @returns {number} - 整数1と整数2の最大公約数 */ function gcd2(m: number, n: number): number { if (n === 0) { return m; } else { return gcd2(n, m % n); } } /** * 複数の整数の最大公約数を求める関数 * @param {...number} ints - 最大公約数を求める整数配列 * @returns {number} - 与えられた整数配列の最大公約数 */ function gcd(...ints: number[]): number { return ints.reduce((x, y) => gcd2(x, y)); } /** * 2つの整数の最小公倍数を求める関数 * @param {number} m - 整数1 * @param {number} n - 整数2 * @returns {number} - 整数1と整数2の最小公倍数 */ function lcm2(m: number, n: number): number { return m * n / gcd2(m, n); } /** * 複数の整数の最小公倍数を求める関数 * @param {...number} ints - 最小公倍数を求める整数配列 * @returns {number} - 与えられた整数配列の最小公倍数 */ function lcm(...ints: number[]): number { return ints.reduce((x, y) => lcm2(x, y)); } /** * テストコード */ function main(): void { console.log(`gcd2(30, 45) => ${gcd2(30, 45)}`); console.log(`gcd(30, 72, 12) => ${gcd(30, 72, 12)}`); console.log(`lcm2(30, 72) => ${lcm2(30, 72)}`); console.log(`lcm(30,42,72) => ${lcm(30,42,72)}`); } main();
二分法
編集/** * 2分法による方程式の数値解を求める関数 * @param {number} low - 下限値 * @param {number} high - 上限値 * @param {(x: number) => number} f - 数値解を求める対象となる関数 * @returns {number} 方程式の数値解 */ function bisection(low: number, high : number, f: (x: number) => number): number { let x = (low + high) / 2; let fx = f(x); if (Math.abs(fx) < +1.0e-10) { return x; } if (fx < 0.0) { low = x; } else { high = x; } return bisection(low, high, f); } /** * テスト用の関数 */ function main() { console.log(bisection(0, 3, (x) => x - 1)); console.log(bisection(0, 3, (x) => x * x - 1)); } main();
- 旧課程(-2012年度)高等学校数学B/数値計算とコンピューター#2分法の例を JavaScript に移植しました。
クラス定義とインスタンス化とメンバー関数
編集/** * Hello クラス * @class * @classdesc Hello クラスは、挨拶文を扱うクラスです。 */ class Hello { /** * 挨拶文に含める文字列 * @type {string} */ public s: string; /** * Hello クラスのインスタンスを作成します。 * @param {string} s - 挨拶文に含める文字列 (省略可能) */ constructor(s: string = "world") { this.s = s; } /** * 挨拶文を文字列で表現する関数 * @returns {string} 挨拶文の文字列 */ public toString(): string { return `Hello ${this.s}!`; } /** * 挨拶文を出力する関数 * @returns {void} */ public print(): void { console.log(this.s); } } /** * テスト用の関数 */ function main(): void { const hello1: Hello = new Hello(); console.log(hello1.toString()); hello1.print(); const hello2: Hello = new Hello("my friend"); console.log(hello2.toString()); hello2.print(); console.log( ` Hello.constructor.name => ${Hello.constructor.name} hello1 => ${hello1} hello2.s => ${hello2.s} ` ); } main();
このコードは、JavaScriptで Hello クラスを定義し、そのクラスを使ってテストを行うためのコードです。Hello クラスは、挨拶文を扱うクラスであり、挨拶文に含める文字列を引数として受け取ります。引数が省略された場合、デフォルトで "world" という文字列を含めます。
Hello クラスには、toString() メソッドと print() メソッドがあります。toString() メソッドは、"Hello [挨拶文に含める文字列]!" という文字列を返し、print() メソッドは、挨拶文に含める文字列をコンソールに出力します。
また、main() 関数を定義し、この関数を呼び出すことで、Hello クラスを使って挨拶文を作成してコンソールに出力するテストを行うことができます。main() 関数の中では、Hello クラスのインスタンスを2つ作成し、それぞれの toString() メソッドと print() メソッドを呼び出しています。また、Hello クラスの constructor.name プロパティをコンソールに出力して、クラス名が取得できることを確認しています。
関連リンク
編集- TypeScript: Typed JavaScript at Any Scale. - 公式サイト
- TypeScript: TS Playground - An online editor for exploring TypeScript and JavaScript
- ^ JSのレベルで出来ません。