TypeScript は JavaScriptの上位互換にあたる言語です。

そのため TypeScript を学習するためには、JavaScriptの基礎知識が必須となります。
TypeScriptを使用することで大規模のアプリケーションや共同開発時にミスの少ないコードを書くことができるようになります。

タイプミスが減りバグが抑制される

TypeScriptは、JavaScriptで起こりうるタイプミスや型付けができないことによって発生するバグを抑制してくれます。


const user = {
    name: 'Hanako',
    gender: 'female'
}
console.log(user.gendre);
//                         ^ Did you mean gender?
    

JavaScriptの場合だとタイプミスがあった場合でもundefined が返却されるだけでしたが、TypeScriptの場合はVSCode上で赤波線で指摘してくれます。

また、TypeScriptをJavaScriptにコンパイル(変換)する際にも、以下のようにエラーを出力してくれます。


$ tsc hello.ts
hello.ts:11:18 - error TS2551: Property 'gendre' does not exist on type '{ name: string; gender: string; }'. Did you mean 'gender'?

11 console.log(user.gendre);
                    ~~~~~~

  hello.ts:9:5
    9     gender: 'female'
          ~~~~~~~~~~~~~~~~
    'gender' is declared here.


Found 1 error.
                

静的型付け

JavaScriptの変数は動的型付けとなり、どんな値でも変数に代入することができます。

しかし、複数人でアプリケーションを構築する場合、予期せぬ値を使用されてしまうことがあります。

例えば、「数値型を引数に取りたい関数に文字列を渡してしまう」などです。

JavaScriptの場合、JSDocなどを記述することで「知らせる」ことはできますが「制限する」ことはできませんでした。

TypeScriptでは予め変数の型を定義できるため、他の値が設定されそうになると「この変数(引数)は数値型なので文字列型は代入できませんよ」と伝えてくれる機能があります。

静的型付けを使用する

新しいコードで書ける

JavaScriptは進化し続けている言語で、それに伴いブラウザのアップデートも行われています。

新しいコードで記述すれば、これまでと比較してシンプルに表現できたり、よりスマートな機能を用いることができます。しかし、ブラウザごとに対応される早さがまばらだったり、古いブラウザもユーザーによって使用され続けることがあるため、一概にすぐ新しいコードに切り替えることはできません。

そこで、新しいコードを古いコードに変換してくれるトランスパイラ(コンパイラ)を使用します。

TypeScriptは、コンパイル時に対象となる ECMAScript のバージョンを設定することができ、コンパイル後は設定したバージョンに準拠したjsファイルを出力します。

コンパイル後に出力するバージョンを指定する

TypeScriptをインストール

npmを使ってTypeScriptをインストールします。


$ npm install -g typescript
    

インストールが完了したら、以下のコマンドでバージョンを確認して、パスが通っていることを確認しましょう。


$ tsc -v
Version 4.0.3
    

もし、command not found: tsc が出たら、ターミナルを再起動して再度試してください。

プロジェクトディレクトリの初期化

package.jsonを作成する

package.json には このプロジェクトの概要やバージョン、ライセンス情報を設定するほか、使用する外部のパッケージや実行スクリプトなどを設定することができます。


$ npm init
                

プロジェクトのルートディレクトリで、上記コマンドを実行すると、ディレクトリ内に package.json というファイルが作成されます。

その際に、質問に答えて行く必要があるのですが、あとで package.json を編集できるのでこの時点では難しく考える必要はないかと思います。

TypeScriptの初期化

プロジェクトのルートディレクトリで、TypeScriptの設定ファイルを作成します。

*npx は npmパッケージの実行 を簡略化したコマンドです。


$ npx tsc --init
message TS6071: Successfully created a tsconfig.json file.
                

実行後に、tsconfig.json が作成されます。

compilerOptions にコンパイル時の対象とするファイルや除外するファイル、オプションを設定します。

tsconfig.json についての詳細な情報は以下を参照してください。
TypeScript: TSConfig Reference – Docs on every TSConfig option

コンパイル後に出力するバージョンを指定する

tsconfig.json の target を 任意のバージョンに設定します。
この例では ES5 に準拠しているコードでjsファイルが出力されます。


"target": "es5"
    
Default: ES3
Allowed: ES3 (default), ES5, ES6/ES2015 (synonomous), ES7/ES2016, ES2017, ES2018, ES2019, ES2020, ESNext

コンパイル時にtslibを使用するようにする

コンパイル実行時にPolyfillが必要な場合、コンパイル後のjsファイルにコードを出力していくとファイルサイズが肥大化してしまうため、tslib から import することで抑制します。

このために、まずは tslib を インストールする必要があります。


$ npm install --save tslib
    

インストール後は node_modules ディレクトリが作成され、tslibパッケージがインストールされていることが確認できると思います。
また、package.json の dependencies には依存関係として以下のように追記されています。


"dependencies": {
    "tslib": "^2.0.1"
}
                

インストールした tslib をコンパイル時に使用するように、tsconfig.jsonimportHelperstrue に設定します。


"importHelpers": true
    

コンパイルする

ファイル単体をコンパイルする

hello.ts ファイルを新しく作成します。

hello.ts
console.log('Hello World!');
    

TypeScriptをJavaScriptにコンパイルします。


$ tsc hello.ts
    

新しくhello.jsが作成されていることが確認できると思います。

もともとconsole.log()だけなので中身は変化ありません。

すべてのファイルをコンパイルする

tsconfig.json の設定を反映させ、すべてのファイルをコンパイルする場合は、以下のコマンドを実行します。


$ tsc
    

静的型付けを定義する

JavaScriptの変数にはどんな型の値でも代入することができるため柔軟に感じることもありますが、バグを引き起こす不安要素の一つでもあります。

TypeScriptを使用すれば、予め型を指定することができます。


function greeting(name: string) {
    console.log(`Hello, ${name}`);
}

let x = 1;
greeting(x);
    

この例では、引数 name を 文字列型に指定しています。
そのあとに 数値を代入した 変数 x を greeting関数に渡そうとしているため、VS Codeでも赤波線が引かれますし、コンパイルをしようとすると以下のようになります。


$ tsc hello.ts
hello.ts:6:10 - error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.

6 greeting(x);
           ~


Found 1 error.
                

オブジェクトのメンバーに型を定義する

オブジェクトのメンバーそれぞれの型を定義するためには interface を使用します。


interface Person {
    name: string,
    gender: string
}

function greeting(user: Person) {
    console.log(`Hello, ${user.gender === 'male' ? 'Mr.' : 'Ms.'}${user.name}`);
}

const user = {
    name: 'Hanako',
    gender: 'female'
}

greeting(user);
                

この例では、Personインターフェースを定義し、greeting関数の引数で型指定を行っています。

クラスを定義する

クラスのメンバー変数には publicprivateprotectedstatic といったアクセス修飾子を設定することができます。

アクセス修飾子

修飾子 使い方 意味
public public name: string; インスタンス化後にクラス内外からアクセスが可能
private private password: string; インスタンス化後にクラスからのみアクセスが可能
protected protected updateData(){} インスタンス化後に継承クラス内およびクラス内からアクセスが可能
static static malePrefix: string = 'Mr.'; インスタンス化されていない静的なクラス内を使用してアクセスが可能

クラスを定義

アクセス修飾子を省略した場合はpublicになります。

メンバー変数の_loginIdにはprivate修飾子を設定しているので、ゲッター(値を取得するメソッド) となる get loginId() を定義しています。このようにすることで、読み取り可能で書き込み不可のプロパティを定義できます。

static修飾子を設定している prefix変数のアクセスには、インスタンス化前のクラスを使用するため、User.malePrefix といった呼び出し方になります。


class User {
    private _loginId: string = '';
    static malePrefix: string = 'Mr.';
    static femalePrefix: string = 'Ms.';

    constructor(public person: Person, loginId: string) {
        this.person = person;
        this._loginId = loginId;
    }

    get loginId() {
        return this._loginId;
    }

    greeting() {
        const prefix = this.person.gender === 'male' ? User.malePrefix : User.femalePrefix;
        console.log(`Hello, ${prefix}${this.person.name}`
        );
    }
}

interface Person {
    name: string,
    gender: string
}

const person = {
    name: 'Hanako',
    gender: 'female'
}

const user: User = new User(person, 'example@rabico.dev');
user.greeting();  // Hello, Ms.Hanako
console.log(user.loginId);  // example@rabico.dev
    

コンパイル後は以下のjsファイルが出力されます。


"use strict";
var User = (function () {
    function User(person, loginId) {
        this.person = person;
        this._loginId = '';
        this.person = person;
        this._loginId = loginId;
    }
    Object.defineProperty(User.prototype, "loginId", {
        get: function () {
            return this._loginId;
        },
        enumerable: false,
        configurable: true
    });
    User.prototype.greeting = function () {
        var prefix = this.person.gender === 'male' ? User.malePrefix : User.femalePrefix;
        console.log("Hello, " + prefix + this.person.name);
    };
    User.malePrefix = 'Mr.';
    User.femalePrefix = 'Ms.';
    return User;
}());
var person = {
    name: 'Hanako',
    gender: 'female'
};
var user = new User(person, 'example@rabico.dev');
user.greeting();
console.log(user.loginId);

    

index.htmlで読み込む場合

読み込むファイルはコンパイル後のjsファイルです。

例えば、index.tsを作成した場合は、コンパイル後に出力されるindex.js を読み込むようにしてください。


<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Demo</title>
</head>
<body>
    <script src="./dist/index.js"></script>
</body>
</html>