TypeScriptの特徴

通常のJavaScript(ES6)と異なる点に重きをおいた特徴


問題点(2021年1月時点:バージョンVersion 4.1.3)

一部のコードにcommonjsに準拠した書き方、es2015に準拠した書き方があり混乱をまねく。 外部クラスファイルを扱う際、importやrequereを使うようだがcommonjsやらes2015やらで書き方が異なるうえ、 exportsが使えない、requireが使えないなどエラーが頻発する。 webpackなどでファイルを一つにまとめる方法が現在主流か?


TypeScriptの基本 | インストールからHello Worldの表示まで

手順

前提条件としてnpmコマンドが使えるようになっていること。
  1. TypeScriptをグローバルインストール
    $ npm install -g typescript
  2. バージョンが表示されたらインストール成功
    $ tsc -V
    Version 4.1.3
    			
  3. 任意プロジェクトのルートディレクトリに移動
    cd ~/git/hello_project
  4. 下記コマンドでプロジェクトのルートディレクトリにtsconfig.jsonが作成される。 tsconfig.jsonファイルではコンパイルの設定を行えるが、別になくてもコンパイルはできるのでこの手順は必須にあらず。
    $ tsc --init
  5. ルートディレクトリに適当にtsファイルを作成しプログラミング。

    test.ts

    
    class HelloWorld
    {
        m_big_buta: string = '大きなブタ2'; // メンバ
    
        /** コンストラクタ
         */
        constructor(str1: string){
            console.log(str1);
        }
    
        // publicメソッド
        public bark()
        {
            let value1: number = 333; // 数値型の宣言
            let str1: string = 'こんにちは世界!'; // 文字列型の宣言
            console.log(value1);
            console.log(str1);
            
            this._working(); // privateメソッドの呼び出し
    
            console.log(this.m_big_buta); // メンバを呼び出してみる
            
        }
    
        // privateメソッド
        private _working(){
            console.log('プライベートで猫が歩く');
        }
    }
    
    var helloWorld = new HelloWorld("Hello World!");
    helloWorld.bark();
    			
  6. test.tsをコンパイルしてtest.jsを出力する。
    $tsc test.ts
  7. あとは、通常のJavaScriptの流れと同じでtest.jsをhtmlファイルで読み込むだけ。終わり。
    
    <html>
    <head>
    	<script src="test.js"></script>
    </head>
    <body>
    	出力はコンソールにて
    </body>
    </html>
    			

TypeScriptの開発環境 TypeScriptをwebpackでコンパイル | npm package.json webpack.config.js

事前条件


TypeScriptの開発環境 (webpackでコンパイルする環境)

  1. プロジェクトのルートへ移動
    $cd ~/git/node_demo/demo2
  2. ルートに下記コマンドでpackage.jsonファイルを作成する。
    npm init
    いくつか質問が聞かれるので適当に入力。
  3. package.jsonのscriptタグには独自コマンドを記述できる。
    独自コマンドにはbashを割り当てることができ、コンパイルやshellの実行に活用できる。
  4. 下記のパッケージをインストール
    $ npm install webpack
    $ npm install webpack-cli
    $ npm install typescript
    $ npm install ts-loader


    パッケージをインストールするとpackage.jsonにも記載される。
  5. ソースファイル置き場としてルートにsrcフォルダを作成。
    一つにまとめたファイルの出力先としてdistフォルダを作成。
    mkdir src
    mkdir dist
  6. webpack.config.jsをルートディレクトリに作成する。

    webpack.config.js

    
    module.exports = {
    	
      // 本番環境ならるproduction(最適化される)、開発環境ならdevelopment(デバッグしやすい)。
      mode: 'development',
    
      // エントリーポイント:スタートファイル
      entry: './src/index.ts',
    
      module: {
        rules: [
          {
            test: /\.ts$/, // コンパイル対象ファイルの拡張子
            use: 'ts-loader', // コンパイルパッケージを指定
          },
        ],
      },
      // ここで指定した拡張子は、import文で拡張子を省略できる。
      resolve: {
        extensions: [
          '.ts', '.js',
        ],
      },
    };
    			
  7. TypeScriptのコンパイル設定ファイルであるtsconfig.jsonをルートディレクトリに作成する。

    tsconfig.json

    
    {
      "compilerOptions": {
        "sourceMap": true,
        "target": "es5",
        "module": "es2015"
      }
    }
    			
  8. srcフォルダ内にソースファイル(tsファイル)を作成する。
    今回はindex.tsとNeko.tsファイルを作成。

    ファイル構成




  9. $ npx webpack
    webpackでsrcに存在するtsファイル群を一つにまとめ、distフォルダにmain.jsとして出力する。
  10. 適当にhtmlページを作成し、先ほど生成したmain.jsを読み込む。
    
    <!DOCTYPE html>
    <html lang="ja">
    <head>
    	<meta charset="UTF-8">
    	<title>TypeScriptとwebpackのDemo2</title>
    	<script src="dist/main.js"></script>
    </head>
    <body>
    	<h3>TypeScriptとwebpackのDemo2</h3>
    	コンソールに出力
    	
    </body>
    </html>
    

    表示画面


  11. 終わり
  12. おまけ

    「yarn build」コマンドでコンパイルできるよう、package.jsonに設定を追加
    {
      "name": "typescript_demo2",
      "version": "1.0.0",
      "description": "TypeScriptの開発環境構築その2",
      "main": "index.js",
      "scripts": {
        "build": "npx webpack",
        "test": "echo \"Hello World!\" && exit 1"
      },
      "repository": {
        "type": "git",
        "url": "git+https://github.com/amaraimusi/node_demo.git"
      },
      "author": "kenji uehara",
      "license": "ISC",
      "bugs": {
        "url": "https://github.com/amaraimusi/node_demo/issues"
      },
      "homepage": "https://github.com/amaraimusi/node_demo#readme",
      "dependencies": {
        "ts-loader": "^8.0.14",
        "typescript": "^4.1.3",
        "webpack": "^5.14.0",
        "webpack-cli": "^4.3.1"
      }
    }
    
    			

    実行例



TypeScriptのクラス

見本クラス

/react_demo2/dev/resources/ts/test/Person.ts

// Person.ts

export class Person {
    private name: string;
    private age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    greet(): string {
        return `Hello, my name is ${this.name} and I am ${this.age} years old.`;
    }
}
	
※exportはjsxのお決まりの書き方のようで、importと対をなす考えのようである。

見本クラスのインスタンス化


import React from 'react';
import ReactDOM from 'react-dom';
import { Person } from './test/Person';


const App = () => {
	
  const john = new Person('John', 25);
 const msg = john.greet();
		
  return (
	<div>${msg}</div>
  );
}

ReactDOM.render(<App />, document.getElementById('react_app'));
	

TypeScriptの基本: 文字列型やint型などプリミティブ型の変数宣言

boolean型:

	let isDone: boolean = false;
	

number型: TypeScriptでは整数も浮動小数点もnumber型を使用します。

	let decimal: number = 6;
	let hex: number = 0xf00d;
	let binary: number = 0b1010;
	let octal: number = 0o744;
	

string型:

	let color: string = "blue";
	color = 'red';  // シングルクォートも使用可能
	
また、テンプレート文字列も利用できます。

	let fullName: string = `Bob Bobbington`;
	let age: number = 37;
	let sentence: string = `Hello, my name is ${fullName}. I'll be ${age + 1} years old next month.`;
	

symbol型: (ES2015からの機能)

	let uniqueSymbol: symbol = Symbol();
	
この型は一意の値を取り扱うときに利用します。重複が合ってはならないキーなどに。

void型: 値を持たないことを示す特別な型です。通常、関数が何も返さない場合に使用されます。

	function warnUser(): void {
	    console.log("This is a warning message");
	}
	


これらは基本的なプリミティブ型です。TypeScriptには、これらの基本型を組み合わせたり、拡張したりするための高度な型システムがあります。 独自の型定義や、ユニオン型、インターフェース、型エイリアスなどの機能を学ぶことで、さらに強力な型安全性を持つコードを書くことができます。


関数の基本

TypeScriptはJavaScriptを拡張した言語なので、関数の基本的な書き方はJavaScriptと同じですが、型が存在するという点で大きな違いがあります。 型を付けることでコードのバグの早期発見ができ、品質や保守性を向上させるされています。

関数の基本形

	function add(x: number, y: number): number {
	    return x + y;
	}
	
上記のTypeScript版では、引数xとyの型をnumberに、返り値の型をnumberに指定しています。

関数式
通常のJavaScriptの関数式
	const add = function(x, y) {
	    return x + y;
	};
	
TypeScriptの関数式

	const add: (x: number, y: number) => number = function(x, y) {
	    return x + y;
	};
	

オプショナルな引数(省略可能な引数)

	function greet(name?: string): void {
	    if (name) {
	        console.log(`Hello, ${name}!`);
	    } else {
	        console.log('Hello!');
	    }
	}
	

関数の引数にはデフォルト値を設定することもできます。

	function greet(name: string = "User"): void {
	    console.log(`Hello, ${name}!`);
	}
	

レストパラメータ
複数の引数を配列として受け取るためには、レストパラメータを使用します。

	function getSum(...numbers: number[]): number {
	    return numbers.reduce((prev, current) => prev + current, 0);
	}
	

クラスのメソッドの基本:public, private, protectedなどのアクセス修飾についても説明

アクセス修飾子

TypeScriptには、public, private, protected の3つのアクセス修飾子があります。
これらのアクセス修飾子によりTypeScriptは、オブジェクト指向プログラミングの基本をしっかりとサポートしており、JavaやC#などの他のオブジェクト指向言語に慣れている方にとっては、非常に馴染みやすい機能となっています。

クラスのメソッドの基本

TypeScriptのクラスは、ES6のクラスの仕様をベースに、静的型チェックやアクセス修飾子などの追加機能を持っています。以下は、クラスとそのメソッドの基本的な例です。

	class Animal {
	    constructor(private name: string) {}
	
	    move(distance: number): void {
	        console.log(`${this.name} moved ${distance} meters.`);
	    }
	}
	上記の例では、Animalというクラスにmoveというメソッドが定義されています。また、コンストラクタ内でnameというプライベートプロパティを定義しています。
	

public (デフォルト):

メンバーに何も指定しない場合、そのメンバーはデフォルトでpublicとなります。つまり、クラスの外からでもアクセス可能です。

	class Animal {
	    public name: string;
	    constructor(name: string) {
	        this.name = name;
	    }
	}
	

private:

privateが指定されたメンバーは、そのクラスの中からのみアクセスできます。

	class Animal {
	    private name: string;
	    constructor(name: string) {
	        this.name = name;
	    }
	}
	

protected:

protectedが指定されたメンバーは、そのクラスおよびサブクラスの中からアクセスできます。

	class Animal {
	    protected name: string;
	    constructor(name: string) {
	        this.name = name;
	    }
	}
	
	class Dog extends Animal {
	    bark(): void {
	        console.log(`Woof! Woof! My name is ${this.name}`);
	    }
	}
	


クラス内における、privateメンバとpublicメンバについて

public (デフォルト)

publicアクセス修飾子は、メンバーが外部からアクセス可能であることを示します。TypeScriptでは、アクセス修飾子を指定しない場合、メンバーはデフォルトでpublicとなります。

	class Person {
	    public name: string;  // 明示的にpublicを指定している
	
	    constructor(name: string) {
	        this.name = name;
	    }
	
	    public greet(): void {  // 明示的にpublicを指定している
	        console.log(`Hello, my name is ${this.name}.`);
	    }
	}
	

private

privateアクセス修飾子は、メンバーがそのクラス内からのみアクセス可能であることを示します。

	class Person {
	    private name: string;
	
	    constructor(name: string) {
	        this.name = name;
	    }
	
	    public greet(): void {
	        console.log(`Hello, my name is ${this.getName()}.`);
	    }
	
	    private getName(): string {  // privateメソッド
	        return this.name;
	    }
	}
	上記の例では、nameプロパティとgetNameメソッドはprivateであり、クラスの外部からは直接アクセスできません。しかし、greetメソッドはpublicなので、外部から呼び出すことができ、その中でgetNameメソッドを使っているのが分かります。
	
また、TypeScript 3.8以降では、「#」をプロパティ名の前に付けることで、そのプロパティをプライベートフィールドとして定義することもできます。

	class Person {
	    #name: string;
	
	    constructor(name: string) {
	        this.#name = name;
	    }
	
	    public greet(): void {
	        console.log(`Hello, my name is ${this.#name}.`);
	    }
	}
	
このプライベートフィールドは、通常のprivateメンバよりも厳格にプライベートであり、サブクラスからもアクセスすることができません。


TypeScriptにおけるオブジェクトとしての連想配列は? | インデックスシグネジャ

TypeScriptでは、連想配列を表現するのに一般的に「オブジェクト」と「インデックスシグネチャ」を使用します。 連想配列のキーと値の型を正確に指定する型という考え方は、少々冗長ですがコードの安全性を向上させることができます。

オブジェクトとしての連想配列


	JavaScriptのオブジェクトリテラルを使用して、簡単に連想配列を作成することができます。
	let obj: { [key: string]: string } = {
	    "key1": "value1",
	    "key2": "value2"
	};
	
ここでは、オブジェクトobjは文字列のキーに対して文字列の値を持つことが示されています。

インデックスシグネチャ

より柔軟に連想配列の型を表現するために、TypeScriptではインデックスシグネチャを使うことができます。

	interface StringDictionary {
	    [key: string]: string;
	}
	
	let obj: StringDictionary = {
	    "key1": "value1",
	    "key2": "value2"
	};
	
このStringDictionaryインターフェースは、任意の文字列のキーを持つことができ、それに対応する値も文字列であることを示しています。

数値のキーを持つ連想配列


	interface NumberDictionary {
	    [key: number]: string;
	}
	
	let arr: NumberDictionary = {
	    1: "value1",
	    2: "value2"
	};
	

TypeScriptのインターフェース

TypeScriptは、JavaやC#のインターフェースと全く同じというわけではないようです。 C#やJavaのインターフェースは、クラスが実装すべきメソッドの定義するのが主な目的です。しかし、TypeScriptはそれに加えてインデックスシグネチャというオブジェクト形状を指定するのにも使われます。

クラスにインターフェースを実装


	interface Animal {
	    name: string;
	    makeSound(): void;
	}
	
	class Dog implements Animal {
	    constructor(public name: string) {}
	
	    makeSound(): void {
	        console.log("Woof! Woof!");
	    }
	}
	
	const myDog = new Dog("Buddy");
	myDog.makeSound();  // "Woof! Woof!"
	
上記の例では、DogクラスはAnimalインターフェースを実装しています。このように、インターフェースはクラスが特定の形状や契約を満たしていることを保証するためのものです。 しかし、TypeScriptのインターフェースはコンパイル時のみの存在であり、コンパイルされたJavaScriptコードには現れません。そのため、実行時にはインターフェースに関する情報は利用できない点を理解しておくことが重要です。

インデックスシグネチャとしてのインターフェースの使い方


	// インターフェースの定義
	interface Person {
	    firstName: string;
	    lastName: string;
	    age?: number;  // オプショナルなプロパティ
	}
	
	// インターフェースを実装した関数
	function greet(person: Person): void {
	    console.log(`Hello, ${person.firstName} ${person.lastName}`);
	}
	
	// インターフェースを実装したオブジェクト
	const john: Person = {
	    firstName: "John",
	    lastName: "Doe"
	};
	
	greet(john);