TypeScript
注記
本書『サバイバルTypeScript』は実務でTypeScriptを使う開発者のための入門書です。そして、このページはTypeScriptの特徴を最速で把握できるよう、数百ページからなる本書のコンテンツをつまみ食いした要約です。
TypeScriptとは
- JavaScriptのスーパーセットとなるプログラミング言語。
- 静的型付け言語であり、プログラムの正しさが静的に検査できる。
- ライブラリやIDEなどの開発環境が充実しており、大きなエコシステムを持っている。
- Microsoftが2012年に開発し、オープンソースで公開した。
» TypeScriptの特徴について詳しく知る
» TypeScript誕生の背景について詳しく知る
TypeScriptはJavaScriptのスーパーセット
- スーパーセットとは、元の言語との互換性を保ちつつ、元の言語を拡張して作った言語のこと。
- TypeScriptは、JavaScriptとの互換性を保ちつつ、JavaScriptを拡張して作った言語である。
- よって、JavaScriptのコードはすべてTypeScriptとしてあつかえる。
- TypeScriptは、型注釈やインターフェース、ジェネリクスなど独自の機能を追加している。
スーパーセットのメリット
- 学習のしやすさ: JavaScriptの知識を活かしてTypeScriptを学べる。
- 資産が活かせる: 既存のJavaScriptコード資産を活かして開発できる。
- 移行のしやすさ: 既存のJavaScriptプロジェクトはTypeScriptへ移行がしやすい。
» TypeScriptとJavaScriptの関係について詳しく知る
静的な検査
- TypeScriptはプログラムの正しさを静的に検査できる。
- JavaScriptは実行しないとバグがあるかを確かめられない。
- TypeScriptは実行せずにチェックが行える。
開発効率と品質を向上し、安心感を高める
- 問題を早期に発見し、開発を効率化できる。
- コーディング時に問題を発見し、修正できるため、バグを予防できる。
- エディターとTypeScriptを連携させると、リアルタイムのチェックやコード補完が可能。
- 問題を早期に修正できることで、製品の信頼感や安心感が高まる。
- 見通しの悪い大規模なプログラムや、重要なシステムの開発では静的な検査が安心材料になる。
検査の仕組み
- TypeScriptの検査は型システムに基づく。
- 型システムに基づき、コンパイルのタイミングでプログラムを検査する。
型システム
- 型システムは、データの種別ごとに型を与え、データに対して行える操作に制約を設ける。
- これにより、変数には決められた値のみが代入され、決められた操作のみが行われることが保証され、プログラムが正確で安全になる。
- 型システムは、数学の「型理論」を背景に構築され、数学的証明によりプログラムの欠陥をあぶり出せる。
型注釈
- 変数にどのような値が代入できるのかを制約するものを「型」と言う。
- 開発者は、変数がどのような型なのかを型注釈で指定する。
- TypeScriptでは、型注釈を手がかりに検査が行われる。
型推論
- 値の型が文脈で明白な場合、型が自動で判断される。この仕組みを型推論という。
- 型推論のおかげで、開発者は型注釈を割愛でき、記述量を減らせる。
コンパイル
- TypeScriptを実行するために、JavaScriptへ変換する。この変換のことをコンパイルという。
- 変換後のJavaScriptコードはブラウザやサーバーで実行できる。
- TypeScriptの検査はコンパイルのタイミングで行われる。
型はドキュメント、リファクタリング、ツールの充実にも寄与
- ドキュメントになる: 型情報はドキュメントの役割を果たし、コードの理解を助ける。
- リファクタリングが安全に: 変数の型や関数のシグネチャを変更したとき、修正が必要な箇所がコンパイル時にすべて分かり、不注意による誤修正を減らせる。
- ツールサポートが充実: IDEやエディターでのリアルタイムのエラーチェック、自動補完、リファクタリングツール、ナビゲーションなど、開発ツールのサポートが充実している。
多くのエディターがTypeScriptをサポート
- Visual Studio Code
- JetBrains IDE (IntelliJ, WebStorm, PhpStorm, RubyMine, PyCharm, GoLandなど)
- Vim
- NeoVim
- Emacs (Tide)
- Atom
- Sublime Text
多様なソフトウェアが作れる
作れるものの範囲が広いことは、TypeScriptの魅力のひとつ。
- Webアプリケーション: TypeScriptの主戦場。フロントエンドの開発に広く使用される。
- サーバーサイドアプリケーション: Node.jsと組み合わせて、バックエンドやAPIサーバーを開発することが可能。
- モバイルアプリケーション: React Nativeなどのフレームワークを利用して、モバイルアプリケーションを開発できる。
- デスクトップアプリケーション: Electronを使用して、クロスプラットフォームのデスクトップアプリを開発できる。
- クラウド関連の機能: AWS LambdaやAzure Functionsなどのクラウドプラットフォームで、サーバーレス関数が作成できる。
- ユーティリティーやCLIツール: コマンドラインツールや各種ユーティリティの開発ができる。
- インフラ構成管理(IaC): PulumiやAWS CDKを使用して、インフラの構成を管理することができる。
- アプリケーションの拡張機能: Google ChromeやVisual Studio Codeなどデスクトップアプリケーションの拡張をTypeScriptで開発できる。
TypeScriptを導入した企業の感想
- Slack: コードベースが大規模になっても、型システムが安全性と信頼性を保証してくれる。
- Airbnb: TypeScriptを使っていたらAirbnbの38%ものバグを未然に防げた。
- ヤフー株式会社: 静的型付けによりコードの品質とメンテナンス性が向上し、IDEとの連携により開発者の生産性が向上した。
- LINE株式会社: ちょっとした修正でもかかるQAのコストを、TypeScript化によって抑制。
- Sansan株式会社: 型がドキュメントとしての役割を果たし、コードリーディングや他チームのコード変更に役立った。採用の文脈でアピールポイントにもなった。
- ラクスル株式会社:型システムの恩恵が得られる、エディターの入力補完を受けられる、コード=ドキュメントという状況を作りやすい。
基本的な型
プリミティブ型
- boolean: 真偽値。
- number: 数値。
- string: 文字列。
- bigint: 大きな整数。
- symbol: 一意の値を示す。
- undefined: 値が定義されていない状態を示す。
- null: 値が存在しない状態を示す。
typescriptisReady : boolean = false;constage : number = 25;constfullName : string = "John Doe";constbigNumber : bigint = 100n;constuniqueSymbol : symbol =Symbol ("unique");constnotDefined : undefined =undefined ;constempty : null = null;
typescriptisReady : boolean = false;constage : number = 25;constfullName : string = "John Doe";constbigNumber : bigint = 100n;constuniqueSymbol : symbol =Symbol ("unique");constnotDefined : undefined =undefined ;constempty : null = null;
特殊な型
- any: 何でも代入できる型。型が不明な場合に使用する。その値に対する操作の制限がなく、型の安全性は弱まる。
- unknown: any型と似て、何でも代入できる型。その値に対する操作は制限され、型の安全性が保たれる。
- void: 値が存在しないことを示す。関数が何も返さない場合に使用する。
- never: 決して何も返さないことを示す。エラーを投げる関数や無限ループの関数の戻り値として使用する。
typescripta : any = 100; // 代入できるconsole .log (a * 3); // 操作もできるconstx : unknown = 100; // 代入はできる'x' is of type 'unknown'.18046'x' is of type 'unknown'.console .log (* 3); // 操作はできない x // 戻り値のない関数functiondoSomething (): void {}// 戻り値を返すことがありえない関数functionthrowError (): never {throw newError ();}
typescripta : any = 100; // 代入できるconsole .log (a * 3); // 操作もできるconstx : unknown = 100; // 代入はできる'x' is of type 'unknown'.18046'x' is of type 'unknown'.console .log (* 3); // 操作はできない x // 戻り値のない関数functiondoSomething (): void {}// 戻り値を返すことがありえない関数functionthrowError (): never {throw newError ();}
型エイリアス
- 型エイリアスは既存の型を新たな名前で定義する機能。
- より複雑な型を簡素に表現したり、コードの可読性を向上するのに役立つ。
typescriptStringOrNumber = string | number;letvalue :StringOrNumber ;value = "hello"; // string型が代入可能value = 123; // number型も代入可能
typescriptStringOrNumber = string | number;letvalue :StringOrNumber ;value = "hello"; // string型が代入可能value = 123; // number型も代入可能
構造的部分型
- TypeScriptは構造的部分型を採用している。
- 構造的部分型では、変数の代入可否を、構造が互換しているかに着目して判定する。
typescriptSummary = {name : string };typeDetail = {name : string;age : number };constjohnDetail :Detail = {name : "John",age : 28 };constsummary :Summary =johnDetail ; // 代入できる。構造的部分型として互換があるためconstjohnSummary :Summary = {name : "John" };constProperty 'age' is missing in type 'Summary' but required in type 'Detail'.2741Property 'age' is missing in type 'Summary' but required in type 'Detail'.: detail Detail =johnSummary ; // 代入できない。構造的部分型として互換がない(ageを含まないため)
typescriptSummary = {name : string };typeDetail = {name : string;age : number };constjohnDetail :Detail = {name : "John",age : 28 };constsummary :Summary =johnDetail ; // 代入できる。構造的部分型として互換があるためconstjohnSummary :Summary = {name : "John" };constProperty 'age' is missing in type 'Summary' but required in type 'Detail'.2741Property 'age' is missing in type 'Summary' but required in type 'Detail'.: detail Detail =johnSummary ; // 代入できない。構造的部分型として互換がない(ageを含まないため)
配列
配列リテラル
- 配列の値を作るには配列リテラル([])を使う。
- [要素1, 要素2, ...]の形で配列の初期値を設定できる。
typescriptnumbers = [1, 2, 3];
typescriptnumbers = [1, 2, 3];
配列の型注釈
- 配列の型注釈には型名[]またはArray<型名>を使う。
typescriptnumbers : number[];letstrings :Array <string>;
typescriptnumbers : number[];letstrings :Array <string>;
配列要素へのアクセス
- 配列要素にアクセスするにはインデックス(インデックス)を使う。
- 0から始まる整数を指定して配列の値を取得し、代入も可能。
typescriptcolors = ["red", "green", "blue"];console .log (colors [0]);colors [1] = "yellow";console .log (colors );
typescriptcolors = ["red", "green", "blue"];console .log (colors [0]);colors [1] = "yellow";console .log (colors );
読み取り専用配列
- 読み取り専用配列は値の変更ができない配列を表す。
- 配列の型注釈にreadonlyをつけると読み取り専用配列となる。
- ReadonlyArray<型名>でも読み取り専用配列が宣言でき、- readonly 型名[]と機能は同じ。
typescriptnumbers : readonly number[] = [1, 2, 3];conststrings :ReadonlyArray <string> = ["hello", "world"];Index signature in type 'readonly number[]' only permits reading.2542Index signature in type 'readonly number[]' only permits reading.numbers [0] = 4; // 値を変更できないProperty 'push' does not exist on type 'readonly string[]'.2339Property 'push' does not exist on type 'readonly string[]'.strings .("!"); // 要素を追加できない push 
typescriptnumbers : readonly number[] = [1, 2, 3];conststrings :ReadonlyArray <string> = ["hello", "world"];Index signature in type 'readonly number[]' only permits reading.2542Index signature in type 'readonly number[]' only permits reading.numbers [0] = 4; // 値を変更できないProperty 'push' does not exist on type 'readonly string[]'.2339Property 'push' does not exist on type 'readonly string[]'.strings .("!"); // 要素を追加できない push 
配列のループ
- 配列をループするためのfor...of構文もある。
typescriptnumbers = [1, 2, 3];for (constnum ofnumbers ) {console .log (num ); // 1, 2, 3と出力される}
typescriptnumbers = [1, 2, 3];for (constnum ofnumbers ) {console .log (num ); // 1, 2, 3と出力される}
タプル型
- タプル型を使うと、配列の要素数と要素の型が固定される。
- それぞれの要素のインデックスごとに型が決まる。
typescripttuple : [string, number];tuple = ["hello", 10]; // 代入できるType 'number' is not assignable to type 'string'.tuple = [10 ,"hello" ]; // 順序が正しくないため、代入できない
Type 'string' is not assignable to type 'number'.2322
2322Type 'number' is not assignable to type 'string'.
Type 'string' is not assignable to type 'number'.Type '[string, number, string]' is not assignable to type '[string, number]'. Source has 3 element(s) but target allows only 2.2322Type '[string, number, string]' is not assignable to type '[string, number]'. Source has 3 element(s) but target allows only 2.= ["hello", 10, "world"]; // 要素が多すぎるため代入できない tuple 
typescripttuple : [string, number];tuple = ["hello", 10]; // 代入できるType 'number' is not assignable to type 'string'.tuple = [10 ,"hello" ]; // 順序が正しくないため、代入できない
Type 'string' is not assignable to type 'number'.2322
2322Type 'number' is not assignable to type 'string'.
Type 'string' is not assignable to type 'number'.Type '[string, number, string]' is not assignable to type '[string, number]'. Source has 3 element(s) but target allows only 2.2322Type '[string, number, string]' is not assignable to type '[string, number]'. Source has 3 element(s) but target allows only 2.= ["hello", 10, "world"]; // 要素が多すぎるため代入できない tuple 
タプルの要素へのアクセス
- タプルの要素にアクセスする場合も配列同様にインデックス(インデックス)を使用する。
typescripttuple : [string, number] = ["hello", 10];console .log (tuple [0]);
typescripttuple : [string, number] = ["hello", 10];console .log (tuple [0]);
オブジェクト
オブジェクトリテラル
- オブジェクトの作り方はオブジェクトリテラル({})を使う。
- { プロパティキー: 値, ... }の形でオブジェクトの初期値を設定できる。
typescriptjohn = {name : "John",age : 20 };
typescriptjohn = {name : "John",age : 20 };
プロパティアクセス
- ドット.を使ってオブジェクトのプロパティにアクセスできる。
typescriptconsole .log (john .name );
typescriptconsole .log (john .name );
オブジェクトの型注釈
- オブジェクトの型注釈は{プロパティ1: 型1, プロパティ2: 型2, ...}の形で記述する。
typescriptobj : {name : string;age : number };
typescriptobj : {name : string;age : number };
readonlyプロパティ
- readonlyをつけたプロパティは代入できない。
typescriptobj : { readonlyname : string;age : number };obj = {name : "John",age : 20 };Cannot assign to 'name' because it is a read-only property.2540Cannot assign to 'name' because it is a read-only property.obj .= "Tom"; name 
typescriptobj : { readonlyname : string;age : number };obj = {name : "John",age : 20 };Cannot assign to 'name' because it is a read-only property.2540Cannot assign to 'name' because it is a read-only property.obj .= "Tom"; name 
オプションプロパティー
- オプショナルプロパティー?をつけたプロパティは省略可能。
typescriptobj : {name : string;age ?: number };obj = {name : "John" }; // `age`プロパティがなくてもエラーにならない
typescriptobj : {name : string;age ?: number };obj = {name : "John" }; // `age`プロパティがなくてもエラーにならない
オブジェクトメソッド
- 関数をプロパティに持つオブジェクトを定義できる。
typescriptobj = {a : 1,b : 2,sum (): number {return this.a + this.b ;},};console .log (obj .sum ());
typescriptobj = {a : 1,b : 2,sum (): number {return this.a + this.b ;},};console .log (obj .sum ());
インデックス型
- オブジェクトはインデックス型を利用して任意のキーの値を取得することができる。
- インデックス型プロパティの型注釈は[キー名: プロパティキーの型]: プロパティ値の型の形で記述する。
typescriptobj : { [key : string]: number };obj = {key1 : 1,key2 : 2 };console .log (obj ["key1"]);console .log (obj ["key2"]);
typescriptobj : { [key : string]: number };obj = {key1 : 1,key2 : 2 };console .log (obj ["key1"]);console .log (obj ["key2"]);
Shorthand property names
- プロパティの値がすでに定義されている変数である場合、そのプロパティ名を省略して記述できる(shorthand property names)。
typescriptname = "John";constage = 20;constobj = {name ,age };console .log (obj );
typescriptname = "John";constage = 20;constobj = {name ,age };console .log (obj );
オプショナルチェーン
- プロパティが存在するかどうか不確定である場合、?.演算子(オプショナルチェーン)で安全にアクセスできる。
typescriptprintLength (obj : {a ?: string }) {console .log (obj .a ?.length );}printLength ({a : "hello" });printLength ({});
typescriptprintLength (obj : {a ?: string }) {console .log (obj .a ?.length );}printLength ({a : "hello" });printLength ({});
Map
Mapオブジェクト
- Mapオブジェクトはキーとそれに対応する値を対にしたコレクション。
- キーはオブジェクトも含め任意の値が可能。
typescriptmap = newMap ();map .set ("name", "John");map .set ("age", "20");console .log (map .get ("name"));
typescriptmap = newMap ();map .set ("name", "John");map .set ("age", "20");console .log (map .get ("name"));
Mapの型注釈
- Mapの型注釈はMap<キーの型, 値の型>の形で記述する。
typescriptpeople :Map <string, number>;
typescriptpeople :Map <string, number>;
Mapのループ
- Mapオブジェクトはfor...ofでループすると、各エントリーがキーと値の配列として順に取得できる。
- 要素の順序は、要素を追加した順が保証されている。
typescriptkey ,value ] ofmap ) {console .log (key ,value );}
typescriptkey ,value ] ofmap ) {console .log (key ,value );}
Set
Set オブジェクト
- Setオブジェクトは同じ値が存在しないコレクション。
- Setの要素は何でも可能である。
typescriptset = newSet ();set .add (1);set .add (2);set .add (2); // 同じ値は追加されない。console .log (set );
typescriptset = newSet ();set .add (1);set .add (2);set .add (2); // 同じ値は追加されない。console .log (set );
Setの型注釈
- Setの型注釈はSet<要素の型>の形で記述する。
typescriptnumSet :Set <number>;
typescriptnumSet :Set <number>;
Setのループ
- SetもMap同様にfor...ofでループすることが可能。
- 順序はaddした順。
typescriptvalue ofset ) {console .log (value );}
typescriptvalue ofset ) {console .log (value );}
列挙型 (Enum)
列挙型の基本
- 列挙型(enum)は、関連する一連の数値または文字列値の集まりを定義する。
- 列挙型はenumキーワードを使用して定義する。
typescriptColor {Red ,Green ,Blue ,}
typescriptColor {Red ,Green ,Blue ,}
列挙型に値を設定
- 列挙体の値は文字列リテラルまたは数値リテラルで指定できる。
typescriptColor {Red = "red",Green = "green",Blue = "blue",}
typescriptColor {Red = "red",Green = "green",Blue = "blue",}
列挙型の利用
- 列挙型の各値にアクセスするにはドット演算子を使用する。
typescriptmyColor :Color =Color .Red ;
typescriptmyColor :Color =Color .Red ;
ユニオン型
- ユニオン型は複数の型のうちのいずれかをとる値を表現できる。
- 型1 | 型2 | ...の形式で使う。
- ひとつ以上の異なる型の値を同じ変数で扱う場合に使用する。
typescriptvalue : boolean | number;value = true; // 代入できるvalue = 100; // 代入できる
typescriptvalue : boolean | number;value = true; // 代入できるvalue = 100; // 代入できる
判別可能なユニオン型
- 判別可能なユニオン型は、共通のリテラル型のプロパティを持つ特別なユニオン型。
- 共通のプロパティを利用して、型を判別できる。
typescriptTriangle = {kind : "triangle";base : number;height : number };typeRectangle = {kind : "rectangle";width : number;height : number };typeShape =Triangle |Rectangle ;functiongetArea (shape :Shape ): number {// 共通のプロパティkindを利用して型を判定するswitch (shape .kind ) {case "triangle":// この節ではshapeがTriangle型に絞り込まれるreturn (shape .base *shape .height ) / 2;case "rectangle":// この節ではshapeがRectangle型に絞り込まれるreturnshape .width *shape .height ;}}
typescriptTriangle = {kind : "triangle";base : number;height : number };typeRectangle = {kind : "rectangle";width : number;height : number };typeShape =Triangle |Rectangle ;functiongetArea (shape :Shape ): number {// 共通のプロパティkindを利用して型を判定するswitch (shape .kind ) {case "triangle":// この節ではshapeがTriangle型に絞り込まれるreturn (shape .base *shape .height ) / 2;case "rectangle":// この節ではshapeがRectangle型に絞り込まれるreturnshape .width *shape .height ;}}
インターセクション型
- インターセクション型は複数の型を1つに結合した新しい型を定義する。
- 型1 & 型2 & ...の形式で使う。
- その結果として生じた型は、それぞれの型が持つすべてのプロパティとメソッドを備えている。
typescriptOctopus = {swims : boolean };typeCat = {nightVision : boolean };typeOctocat =Octopus &Cat ;constoctocat :Octocat = {swims : true,nightVision : true };console .log (octocat );
typescriptOctopus = {swims : boolean };typeCat = {nightVision : boolean };typeOctocat =Octopus &Cat ;constoctocat :Octocat = {swims : true,nightVision : true };console .log (octocat );
分割代入
- 分割代入を使うと、配列の各要素を一度に変数に代入できる(配列の分割代入)。
typescripta ,b ] = [1, 2];console .log (a );console .log (b );
typescripta ,b ] = [1, 2];console .log (a );console .log (b );
- 分割代入により、オブジェクトのプロパティを個別の変数へ代入できる(オブジェクトの分割代入)。
typescriptobj = {name : "John",age : 20,};const {name ,age } =obj ;console .log (name );console .log (age );
typescriptobj = {name : "John",age : 20,};const {name ,age } =obj ;console .log (name );console .log (age );
条件分岐
- TypeScriptではJavaScriptと同様に、条件分岐にはif構文やswitch構文が利用できる。
if-else文
typescriptage : number = 20;if (age >= 20) {console .log ("You are an adult.");} else {console .log ("You are a minor.");}
typescriptage : number = 20;if (age >= 20) {console .log ("You are an adult.");} else {console .log ("You are a minor.");}
switch文
typescriptcolor : string = "blue";switch (color ) {case "red":console .log ("Color is red.");break;case "blue":console .log ("Color is blue.");break;default:console .log ("Color is neither red nor blue.");}
typescriptcolor : string = "blue";switch (color ) {case "red":console .log ("Color is red.");break;case "blue":console .log ("Color is blue.");break;default:console .log ("Color is neither red nor blue.");}
型の絞り込み
- 条件分岐を利用すると、その節内では型が自動的に絞り込まれる(制御フロー分析と型ガードによる型の絞り込み)。
typescriptvalue : string | number;// 50%の確率でstring型またはnumber型の値を代入するvalue =Math .random () < 0.5 ? "Hello" : 100;if (typeofvalue === "string") {// この節ではvalueはstring型として扱われるconsole .log (value .toUpperCase ());} else {// この節ではvalueはnumber型として扱われるconsole .log (value * 3);}
typescriptvalue : string | number;// 50%の確率でstring型またはnumber型の値を代入するvalue =Math .random () < 0.5 ? "Hello" : 100;if (typeofvalue === "string") {// この節ではvalueはstring型として扱われるconsole .log (value .toUpperCase ());} else {// この節ではvalueはnumber型として扱われるconsole .log (value * 3);}
関数
- TypeScriptではアロー関数や関数宣言に型注釈をつけることができる。
アロー関数
typescriptgreet = (name : string): string => {return `Hello ${name }`;};console .log (greet ("John"));
typescriptgreet = (name : string): string => {return `Hello ${name }`;};console .log (greet ("John"));
関数宣言
typescriptgreet (name : string): string {return `Hello ${name }`;}console .log (greet ("John"));
typescriptgreet (name : string): string {return `Hello ${name }`;}console .log (greet ("John"));
分割代入引数
- 関数の引数に配列またはオブジェクトリテラルを展開することができる(分割代入引数)。
typescriptprintCoord = ({x ,y }: {x : number;y : number }) => {console .log (`Coordinate is (${x }, ${y })`);};printCoord ({x : 10,y : 20 });
typescriptprintCoord = ({x ,y }: {x : number;y : number }) => {console .log (`Coordinate is (${x }, ${y })`);};printCoord ({x : 10,y : 20 });
型ガード関数
- 特定の型であることを判定する関数(型ガード関数)を利用することで、型が絞り込まれる。
typescriptisString (value : any):value is string {return typeofvalue === "string";}functionprintLength (value : any) {if (isString (value )) {// この節ではvalueはstring型として扱われるconsole .log (value .length );}}printLength ("hello");
typescriptisString (value : any):value is string {return typeofvalue === "string";}functionprintLength (value : any) {if (isString (value )) {// この節ではvalueはstring型として扱われるconsole .log (value .length );}}printLength ("hello");
オプション引数
- 関数の引数には?をつけることで任意とすることができる(オプション引数)。
typescriptgreet (name ?: string) {if (name ===undefined ) {return "Hello!";} else {return `Hello ${name }!`;}}console .log (greet ("John"));console .log (greet ());
typescriptgreet (name ?: string) {if (name ===undefined ) {return "Hello!";} else {return `Hello ${name }!`;}}console .log (greet ("John"));console .log (greet ());
デフォルト引数
- 関数の引数には=を使ってデフォルトの値を設定することができる(デフォルト引数)。
typescriptgreet (name : string = "Mystery") {return `Hello ${name }!`;}console .log (greet ("John"));console .log (greet ());
typescriptgreet (name : string = "Mystery") {return `Hello ${name }!`;}console .log (greet ("John"));console .log (greet ());
残余引数
- ...を使って残余引数(任意の数の引数)を設定することができる。
typescriptsum (...numbers : number[]) {returnnumbers .reduce ((total ,num ) =>total +num , 0);}console .log (sum (1, 2, 3, 4, 5));
typescriptsum (...numbers : number[]) {returnnumbers .reduce ((total ,num ) =>total +num , 0);}console .log (sum (1, 2, 3, 4, 5));
クラス
クラス構文
typescriptPerson {name : string;age : number;constructor(name : string,age : number) {this.name =name ;this.age =age ;}introduce (): void {console .log (`My name is ${this.name } and I am ${this.age } years old.`);}}constjohn = newPerson ("John", 20);john .introduce ();
typescriptPerson {name : string;age : number;constructor(name : string,age : number) {this.name =name ;this.age =age ;}introduce (): void {console .log (`My name is ${this.name } and I am ${this.age } years old.`);}}constjohn = newPerson ("John", 20);john .introduce ();
アクセス修飾子
- public(デフォルト)、- protected、- privateの3つのアクセス修飾子が利用できる。
typescriptPerson {publicname : string;privateage : number;constructor(name : string,age : number) {this.name =name ;this.age =age ;}introduce (): void {console .log (`My name is ${this.name } and I am ${this.age } years old.`);}}constjohn = newPerson ("John", 20);console .log (john .name ); // 'John'が出力されるProperty 'age' is private and only accessible within class 'Person'.2341Property 'age' is private and only accessible within class 'Person'.console .log (john .); // エラー(privateなのでアクセスできない) age 
typescriptPerson {publicname : string;privateage : number;constructor(name : string,age : number) {this.name =name ;this.age =age ;}introduce (): void {console .log (`My name is ${this.name } and I am ${this.age } years old.`);}}constjohn = newPerson ("John", 20);console .log (john .name ); // 'John'が出力されるProperty 'age' is private and only accessible within class 'Person'.2341Property 'age' is private and only accessible within class 'Person'.console .log (john .); // エラー(privateなのでアクセスできない) age 
クラスのreadonly修飾子
- readonly修飾子をつけたプロパティは、読み取り専用となる。
- readonly修飾子はアクセス修飾子と併用可能。
typescriptPerson {readonlyname : string;private readonlyage : number;constructor(name : string,age : number) {this.name =name ;this.age =age ;}introduce (): void {console .log (`My name is ${this.name } and I am ${this.age } years old.`);}}constjohn = newPerson ("John", 20);Cannot assign to 'name' because it is a read-only property.2540Cannot assign to 'name' because it is a read-only property.john .= "Tom"; // エラー(readonlyのため変更不可) name 
typescriptPerson {readonlyname : string;private readonlyage : number;constructor(name : string,age : number) {this.name =name ;this.age =age ;}introduce (): void {console .log (`My name is ${this.name } and I am ${this.age } years old.`);}}constjohn = newPerson ("John", 20);Cannot assign to 'name' because it is a read-only property.2540Cannot assign to 'name' because it is a read-only property.john .= "Tom"; // エラー(readonlyのため変更不可) name 
Constructor shorthand
- TypeScriptでは、コンストラクタパラメータにアクセス修飾子をつけることで、自動的にそのフィールドが定義される(constructor shorthand)。
- これによりコードの簡略化が図れる。
typescriptPerson {constructor(publicname : string, privateage : number) {}introduce (): void {console .log (`My name is ${this.name } and I am ${this.age } years old.`);}}constjohn = newPerson ("John", 20);john .introduce ();
typescriptPerson {constructor(publicname : string, privateage : number) {}introduce (): void {console .log (`My name is ${this.name } and I am ${this.age } years old.`);}}constjohn = newPerson ("John", 20);john .introduce ();
フィールドの初期化子
- フィールド宣言の際に直接初期値を設定できる(フィールドの初期化子)。
typescriptCounter {count = 0; // 初期値を0に設定// ^^^初期化子increment (): void {this.count ++;}}constcounter = newCounter ();console .log (counter .count );counter .increment ();console .log (counter .count );
typescriptCounter {count = 0; // 初期値を0に設定// ^^^初期化子increment (): void {this.count ++;}}constcounter = newCounter ();console .log (counter .count );counter .increment ();console .log (counter .count );
静的フィールドと静的メソッド
typescriptMyClass {staticx = 0;staticprintX (): void {console .log (MyClass .x );}}MyClass .printX ();
typescriptMyClass {staticx = 0;staticprintX (): void {console .log (MyClass .x );}}MyClass .printX ();
this型
- メソッド内でthisを返すことで、メソッドの呼び出しを直列につなげるメソッドチェーンを可能にする(メソッドチェーン)。
typescriptMyClass {value = 1;increment (): this {this.value ++;return this;}add (v : number): this {this.value +=v ;return this;}console .log (this.value );return this;}}newMyClass ().increment ().add (3).
typescriptMyClass {value = 1;increment (): this {this.value ++;return this;}add (v : number): this {this.value +=v ;return this;}console .log (this.value );return this;}}newMyClass ().increment ().add (3).
クラスの継承
- extendsキーワードにより、クラスの継承が可能。
- スーパークラスのプロパティ・メソッドの値は、サブクラスからアクセス可能。
typescriptAnimal {name : string;constructor(name : string) {this.name =name ;}greet (): string {return `Hello, my name is ${this.name }`;}}classDog extendsAnimal {bark (): string {return "Woof!";}}constdog = newDog ("Max");console .log (dog .greet ());console .log (dog .bark ());
typescriptAnimal {name : string;constructor(name : string) {this.name =name ;}greet (): string {return `Hello, my name is ${this.name }`;}}classDog extendsAnimal {bark (): string {return "Woof!";}}constdog = newDog ("Max");console .log (dog .greet ());console .log (dog .bark ());
instanceof演算子
- instanceof演算子は、オブジェクトが特定のクラスのインスタンスであるかを判定できる。
typescriptAnimal {}classDog extendsAnimal {}constdog = newDog ();console .log (dog instanceofDog );console .log (dog instanceofAnimal );
typescriptAnimal {}classDog extendsAnimal {}constdog = newDog ();console .log (dog instanceofDog );console .log (dog instanceofAnimal );
抽象クラス
- abstractキーワードにより、抽象クラスを定義できる。
- 抽象クラスはインスタンス化できず、他のクラスが継承するための基底クラスに使用される。
typescriptAnimal {abstractmakeSound (): void;move (): void {console .log ("roaming the earth...");}}classDog extendsAnimal {makeSound (): void {console .log ("Woof Woof");}}constdog = newDog ();dog .move ();dog .makeSound ();
typescriptAnimal {abstractmakeSound (): void;move (): void {console .log ("roaming the earth...");}}classDog extendsAnimal {makeSound (): void {console .log ("Woof Woof");}}constdog = newDog ();dog .move ();dog .makeSound ();
ゲッターとセッター
- ゲッターやセッターは、オブジェクトのプロパティを取得・設定するためのメソッド。
- ゲッターはgetキーワードで、セッターはsetキーワードで定義する。
typescriptCircle {private_radius : number;constructor(radius : number) {this._radius =radius ;}// ゲッターgetradius (): number {return this._radius ;}// セッターsetradius (radius : number) {if (radius <= 0) {throw newError ("Invalid radius value");}this._radius =radius ;}}constcircle = newCircle (5);console .log (circle .radius );circle .radius = 3;console .log (circle .radius );circle .radius = -2;// 例外: 'Invalid radius value'
typescriptCircle {private_radius : number;constructor(radius : number) {this._radius =radius ;}// ゲッターgetradius (): number {return this._radius ;}// セッターsetradius (radius : number) {if (radius <= 0) {throw newError ("Invalid radius value");}this._radius =radius ;}}constcircle = newCircle (5);console .log (circle .radius );circle .radius = 3;console .log (circle .radius );circle .radius = -2;// 例外: 'Invalid radius value'
インターフェース
- TypeScriptのインターフェースは、プロパティ、メソッド、クラスなどの形状を定義する能力を持つ。
- インターフェースを使用する主な目的は、特定のクラスまたはオブジェクトが特定のプロパティまたはメソッドを保持することを強制する。
typescriptPrintable {}classMyClass implementsPrintable {console .log ("Hello, world!");}}
typescriptPrintable {}classMyClass implementsPrintable {console .log ("Hello, world!");}}
インターフェース構文
- TypeScriptのインターフェースはオブジェクトの形状を定義することが可能。
- インターフェースはプロパティやメソッドのシグネチャを記述できる。
typescriptPoint {readonlyx : number;readonlyy : number;sum (): number;}constpoint :Point = {x : 10,y : 20,sum : function () {return this.x + this.y ;},};
typescriptPoint {readonlyx : number;readonlyy : number;sum (): number;}constpoint :Point = {x : 10,y : 20,sum : function () {return this.x + this.y ;},};
インターフェースのreadonly修飾子
- インターフェース内でreadonly修飾子を使用して、プロパティを読み取り専用に設定できる。
- これにより、プロパティの値が一旦設定されると後から変更できなくなる。
typescriptPoint {readonlyx : number;readonlyy : number;}constp1 :Point = {x : 10,y : 20 };Cannot assign to 'x' because it is a read-only property.2540Cannot assign to 'x' because it is a read-only property.p1 .= 5; x 
typescriptPoint {readonlyx : number;readonlyy : number;}constp1 :Point = {x : 10,y : 20 };Cannot assign to 'x' because it is a read-only property.2540Cannot assign to 'x' because it is a read-only property.p1 .= 5; x 
例外処理
- TypeScriptでは例外処理のためにtry / catch / finally ブロックを使用できる。
- 例外が発生した場合(つまり、エラーオブジェクトをスローした場合)catchブロックが実行される。
typescriptError ("An error occurred!");} catch (error ) {console .log (error );}
typescriptError ("An error occurred!");} catch (error ) {console .log (error );}
try-catch-finally構文
- tryブロック内のコードは、エラーを検出し、catchブロックはエラーをハンドリングする。
- finallyブロックはエラーの有無に関係なく実行される。
typescriptError ("Oops, something went wrong.");} catch (error ) {console .log (error );} finally {console .log ("This is the finally block. It always gets executed.");}
typescriptError ("Oops, something went wrong.");} catch (error ) {console .log (error );} finally {console .log ("This is the finally block. It always gets executed.");}
例外クラス
- TypeScriptでは、カスタムエラークラスを作成することも可能。
- Errorクラスを継承したカスタムクラスで、具体的なエラータイプを作成することができる。
typescriptCustomError extendsError {code = "CustomError";constructor(message ?: string) {super(message );}}try {throw newCustomError ("This is a custom error");} catch (error ) {if (error instanceofCustomError ) {console .log (`${error .code }: ${error .message }`);}}
typescriptCustomError extendsError {code = "CustomError";constructor(message ?: string) {super(message );}}try {throw newCustomError ("This is a custom error");} catch (error ) {if (error instanceofCustomError ) {console .log (`${error .code }: ${error .message }`);}}
非同期処理
- TypeScriptでは、非同期プログラミングをサポートしていて、コード内で時間を要する処理を効率的に扱うことができる。
Promise
- Promiseは非同期操作の最終的な完了(または失敗)とその結果の値を表す。
typescriptpromise = newPromise ((resolve ,reject ) => {setTimeout (() => {resolve ("Promise resolved");}, 2000);});promise .then ((data ) => {console .log (data );});
typescriptpromise = newPromise ((resolve ,reject ) => {setTimeout (() => {resolve ("Promise resolved");}, 2000);});promise .then ((data ) => {console .log (data );});
async/await 構文
typescriptdelay (ms : number) {return newPromise ((resolve ) =>setTimeout (resolve ,ms ));}async functionasyncFunction () {console .log ("Start");awaitdelay (2000);console .log ("End");}asyncFunction ();// 2秒後
typescriptdelay (ms : number) {return newPromise ((resolve ) =>setTimeout (resolve ,ms ));}async functionasyncFunction () {console .log ("Start");awaitdelay (2000);console .log ("End");}asyncFunction ();// 2秒後
ジェネリクス
typescriptidentity <T >(arg :T ):T {returnarg ;}// 型変数Tにstringを割り当てるconstoutput1 =identity <string>("myString");// 型変数Tにnumberを割り当てるconstoutput2 =identity <number>(100);
typescriptidentity <T >(arg :T ):T {returnarg ;}// 型変数Tにstringを割り当てるconstoutput1 =identity <string>("myString");// 型変数Tにnumberを割り当てるconstoutput2 =identity <number>(100);
モジュール
- TypeScriptのモジュールシステムは、他のモジュールと共有するコードと、モジュール内部限定のコードとを分けることを可能にする(モジュール)。
greeter.tstypescriptgreet (name : string) {return `Hello, ${name }!`;}
greeter.tstypescriptgreet (name : string) {return `Hello, ${name }!`;}
main.tstypescriptgreet } from "./greeter";console .log (greet ("TypeScript"));
main.tstypescriptgreet } from "./greeter";console .log (greet ("TypeScript"));
importとexport
- モジュール内で定義した関数や変数を外部に公開するには、exportを使用する。
- モジュールが公開した関数や変数を利用するには、importを使用する。
math.tstypescriptsquare (x : number) {returnx *x ;}export functioncube (x : number) {returnx *x *x ;}
math.tstypescriptsquare (x : number) {returnx *x ;}export functioncube (x : number) {returnx *x *x ;}
main.tstypescriptsquare ,cube } from "./math";console .log (square (2));console .log (cube (2));
main.tstypescriptsquare ,cube } from "./math";console .log (square (2));console .log (cube (2));
default export
- defaultキーワードを使用すると、モジュールがデフォルトで1つの値のみをエクスポートすることを意味する。
- default exportは、importする際に別名を指定することが可能である。
greeter.tstypescriptgreet (name : string) {return `Hello, ${name }!`;}
greeter.tstypescriptgreet (name : string) {return `Hello, ${name }!`;}
main.tstypescriptgreetFunction from "./greeter";console .log (greetFunction ("TypeScript"));
main.tstypescriptgreetFunction from "./greeter";console .log (greetFunction ("TypeScript"));
再export
- モジュールは、別のモジュールからエクスポートされたものを再エクスポートすることができる。
math.tstypescriptadd (x : number,y : number) {returnx +y ;}
math.tstypescriptadd (x : number,y : number) {returnx +y ;}
index.tstypescriptadd } from "./math";
index.tstypescriptadd } from "./math";
main.tstypescriptadd } from "./index";console .log (add (2, 3));
main.tstypescriptadd } from "./index";console .log (add (2, 3));
type importとtype export
- 型だけをエクスポート・インポートすることもできる。
types.tstypescriptMyObject = {name : string;age : number;};
types.tstypescriptMyObject = {name : string;age : number;};
main.tstypescriptMyObject } from "./types";// ^^^^型インポートconstobj :MyObject = {name : "TypeScript",age : 3,};
main.tstypescriptMyObject } from "./types";// ^^^^型インポートconstobj :MyObject = {name : "TypeScript",age : 3,};
型レベルプログラミング
- TypeScriptには、typeof演算子やkeyof演算子、ユーティリティータイプなど、型レベルでプログラミングをするためのさまざまな機能が搭載されている。
typeof型演算子
- typeof演算子は、変数名から型を逆算できる。
typescriptobject = {name : "TypeScript",version : 3.9,};typeObjectType = typeofobject ;
typescriptobject = {name : "TypeScript",version : 3.9,};typeObjectType = typeofobject ;
keyof型演算子
- keyof演算子を使うと、object型のすべてのキーを文字列リテラルのユニオン型として取得できる。
typescriptPoint = {x : number;y : number;};typeKey = keyofPoint ;constkey1 :Key = "x"; // 代入OKconstkey2 :Key = "y"; // 代入OKconstType '"z"' is not assignable to type 'keyof Point'.2322Type '"z"' is not assignable to type 'keyof Point'.: key3 Key = "z"; // 代入不可
typescriptPoint = {x : number;y : number;};typeKey = keyofPoint ;constkey1 :Key = "x"; // 代入OKconstkey2 :Key = "y"; // 代入OKconstType '"z"' is not assignable to type 'keyof Point'.2322Type '"z"' is not assignable to type 'keyof Point'.: key3 Key = "z"; // 代入不可
ユーティリティ型
- TypeScriptは、既存の型から新しい型を作成するためのさまざまな一般的な型操作を提供している。
Required
- Requiredは、オプションプロパティーを必須プロパティーにするユーティリティ型。
typescriptPerson = {name : string;age ?: number;};typeRequiredPerson =Required <Person >;// ageがオプションでなくなっている点に注目
typescriptPerson = {name : string;age ?: number;};typeRequiredPerson =Required <Person >;// ageがオプションでなくなっている点に注目
Partial
- Partialは、型のすべてのプロパティをオプションにするユーティリティ型。
typescriptPerson = {name : string;age : number;};typeOptionalPerson =Partial <Person >;
typescriptPerson = {name : string;age : number;};typeOptionalPerson =Partial <Person >;
Readonly
- Readonlyは、型のすべてのプロパティをreadonlyにするユーティリティ型。それらのプロパティは再代入できない。
typescriptPerson = {name : string;age : number;};typeReadonlyPerson =Readonly <Person >;
typescriptPerson = {name : string;age : number;};typeReadonlyPerson =Readonly <Person >;
Record
- Recordは、オブジェクトのすべてのプロパティ値を特定の型に設定するユーティリティ型。
typescriptThreeLetterRecord =Record <"one" | "two" | "three", string>;
typescriptThreeLetterRecord =Record <"one" | "two" | "three", string>;
Pick
- Pickは、オブジェクトから特定のプロパティだけを拾い出すユーティリティ型。
typescriptPerson = {name : string;age : number;address : string;};typePersonNameAndAge =Pick <Person , "name" | "age">;
typescriptPerson = {name : string;age : number;address : string;};typePersonNameAndAge =Pick <Person , "name" | "age">;
Omit
- Omitは、オブジェクトから特定のプロパティを省いた型を作るユーティリティ型。
typescriptPerson = {name : string;age : number;address : string;};typePersonWithoutAddress =Omit <Person , "address">;
typescriptPerson = {name : string;age : number;address : string;};typePersonWithoutAddress =Omit <Person , "address">;
Exclude
- Excludeは、ユニオン型から特定の型を除外するユーティリティ型。
typescriptT1 = number | string | boolean;typeT2 =Exclude <T1 , boolean>;
typescriptT1 = number | string | boolean;typeT2 =Exclude <T1 , boolean>;
Extract
- Extractは、ふたつのユニオン型の共通の部分を抽出するユーティリティ型。
typescriptT1 = number | string | boolean;typeT2 = string | boolean;typeT3 =Extract <T1 ,T2 >;
typescriptT1 = number | string | boolean;typeT2 = string | boolean;typeT3 =Extract <T1 ,T2 >;
NonNullable
- NonNullableは、nullまたはundefinedを含む型からいずれも除外するユーティリティ型。
typescriptT1 = string | null | undefined;typeT2 =NonNullable <T1 >;
typescriptT1 = string | null | undefined;typeT2 =NonNullable <T1 >;
Mapped types
- Mapped typesを使うと、既存の型から新しい型を生成できる。
- Mapped typesは、オブジェクトの各プロパティを”マップ”し、新しいオブジェクトを生成する。
typescriptPerson = {name : string;age : number;};typeReadOnlyPerson = { readonly [K in keyofPerson ]:Person [K ] };
typescriptPerson = {name : string;age : number;};typeReadOnlyPerson = { readonly [K in keyofPerson ]:Person [K ] };
インデックスアクセス型
- インデックスアクセス型を使うと、型のプロパティの型を取得できる。
typescriptPerson = {name : string;age : number;};typeName =Person ["name"];
typescriptPerson = {name : string;age : number;};typeName =Person ["name"];