TypeScriptを使いたいけど通常のJavaScriptとどんな違いがあるのと疑問に思っている人もいるかと思います。本文書では公式のドキュメントを参考し入門者を対象としてTypeScriptの基本について説明を行っています。

本文書を読み終えるとTypeScriptの型注釈(Type Annotation)とInterfaceの基礎を理解することができます。

TypeScriptのインストール

参考にするのはドキュメントのTypeScript in 5 minutesです。

npmコマンドでグローバルにtypescriptのインストールを行います。


 $ npm install -g typescript

最も簡単な例を使った動作確認

テストデータの作成

次にgreeter.tsファイルを作成し下記のコードを記述します。TypeScriptのファイルの拡張子はtsです。


function greeter(person) {
  return "Hello, " + person;
}

let user = "Jane User";

document.body.textContent = greeter(user);

はじめてのコンパイル

greeter.tsファイルを作成後TypeScriptからJavaScriptファイルへの変換を行う必要があります。変換はtscコマンドを利用します。


 $ tsc greeter.ts 

実行が完了するとtscコマンドを実行したディレクトリ内にgreeter.jsファイルが作成されます。


function greeter(person) {
    return "Hello, " + person;
}
var user = "John Smith";
document.body.textContent = greeter(user);
実際にgreet.jsファイルをindex.htmlファイルのscriptタグで読み込むと画面にJane Userが表示されます。変換前のgreeter.tsファイルをscriptタグで指定しても同じ結果になります。

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

Type Annotation(型注釈)の追加

次にgreeter関数のpersonに型注釈を追加します。stringと追加したことでgreeterに文字列を渡さなければエラーとなります。この設定によりpersonには文字列が入るということを宣言したことになります。


function greeter(person: string) {
  return "Hello, " + person;
}

stringを追加してもこれまでのコードであればuserは文字列の”Jane User”なのでエラーが表示されることもなくtscコマンドは型注釈を追加する前と同様にコンパイルも完了します。

しかし、意図的にuserに配列を渡してtscコマンドでコンパイルを実行するとエラーが発生します。


let user = [0, 1, 2];

以下のように数値で構成された配列を入れることができないとエラー表示されています。


 $ tsc greeter.ts 
greeter.ts:7:37 - error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.

7 document.body.textContent = greeter(user);
                                      ~~~~
Found 1 error.

※エラーが出力されてもJavaScriptへの変換は完了しgreeter.jsファイルは作成されます。

作成されるgreeter.jsファイルをindex.htmlファイルのscriptタグで読み込むと画面には、Hello, 0,1,2と表示され実行できないわけではありません。

型注釈を入れたgreeter.tsファイルをindex.htmlのscriptタグで読み込むとUncaught SyntaxError: Unexpected token ‘:’のエラーが発生します。

ここまでの動作でTypeScriptはJavaScriptに変換しないと利用することができないこと型注釈を設定することで誤った型のデータ挿入を未然に防ぐことができることが確認できました。

Interface(インターフェイス)とは

TypeScriptのInterfaceはオブジェクトの形を決めるルールのようなものでプロパティの名前と型を定義することができます。

Interfaceの最もシンプルな使い方

例えばPersonというInterfaceを作成します。


interface Person {
    firstName: string;
    lastName: string;
}

PersonはfirstNameとlastNameの2つのプロパティを持ち、どちらの型もstringを持ちます。

先程のgreeter.tsファイルのgreeter関数の引数をオブジェクトに変更し、そのオブジェクトの型注釈に作成したインターフェイスを利用することができます。


function greeter(person: Person) {
  return "Hello, " + person.firstName + " " + person.lastName;
}

Interfaceを型注釈で指定するとgreeter関数を実行する際に挿入するオブジェクトはfirstNameとlastNameを持ちその型はstringである必要があります。


let user = { firstName: "Jane", lastName: "User" };

document.body.textContent = greeter(user);

ここまでのコードをまとめてコンパイルを実行するとこれまでと同様にindex.htmlでgreeter.jsファイルを読み込むと画面上にはHello Jane Userが表示されます。


interface Person {
  firstName: string;
  lastName: string;
}

function greeter(person: Person) {
  return "Hello, " + person.firstName + " " + person.lastName;
}

let user = { firstName: "Jane", lastName: "User" };

document.body.textContent = greeter(user);

プロパティ名や値を変更してエラーを確認

もしfirstNameではなくFullNameにした場合にどのようなエラーになるか確認しておきましょう。


let user = { fullName: "Jane", lastName: "User" };

コンパイルを実行するとプロパティのfirstNameがないとエラーが表示されます。


$ tsc greeter.ts 
greeter.ts:12:37 - error TS2345: Argument of type '{ fullName: string; lastName: string; }' is not assignable to parameter of type 'Person'.
  Property 'firstName' is missing in type '{ fullName: string; lastName: string; }' but required in type 'Person'.

次にlastNameの値を数字にしてみましょう。


let user = { firstName: "Jane", lastName: 20 };

コンパイルを実行すると先程のプロパティ名を変更した時と異なるエラーが表示されます。値がnumberなのでstringではないというエラーになっています。


 $ tsc greeter.ts 
greeter.ts:13:37 - error TS2345: Argument of type '{ firstName: string; lastName: number; }' is not assignable to parameter of type 'Person'.
  Types of property 'lastName' are incompatible.
    Type 'number' is not assignable to type 'string'.

このようにInterfaceは設計図のような役割を持ちその設計図通りになっていない場合にはエラーが表示されます。

変数userを設定する場合にもInterfaceを利用することができます。


let user: Person = {
    firstName: "John",
    lastName: "Smith"
}

ClassにInterfaceを利用する

Classを作成する際にInterfaceを利用することができます。

作成したInterfaceを利用してClassを作成したい場合はimplementsを使って記述することができます。

Interfaceは先程設計図のような役割といったとおりInterfaceをimplementsしたクラスStudentはfirstNameとlastNameを持つ必要があります。


interface Person {
    firstName: string;
    lastName: string;
}

class Student implements Person {
  public firstName: string;
  public lastName: string;
}

// publicがなくてもOK
class Student implements Person {
  firstName: string;
  lastName: string;
}

プロパティがない場合

もしlastNameがない場合はコンパイル時にエラーが発生します。


class Student implements Person {
  public firstName: string;
}

 $ tsc greeter.ts 
greeter.ts:20:7 - error TS2420: Class 'Student' incorrectly implements interface 'Person'.
  Property 'lastName' is missing in type 'Student' but required in type 'Person

publicではなくprivateを使用した場合

publicではなくprivateを指定した場合も確認しておきましょう。


class Student implements Person {
  private firstName: string;
  public lastName: string;
}

以下のようにprivateに設定していることでエラーが発生しています。


 $ tsc greeter.ts 
greeter.ts:25:7 - error TS2420: Class 'Student' incorrectly implements interface 'Person'.
  Property 'firstName' is private in type 'Student' but not in type 'Person'.

プロパティに”?”がついた場合(optional properties)

地域によっては名前にmiddleNameを使う場合もあります。その場合に備えmiddleNameを追加したInterfaceを作成します。追加した場合はInterface Personをimplementsする際にはプロパティにmiddleNameが必要となります。


interface Person {
  firstName: string;
  middleName: string;
  lastName: string;
}

class Student implements Person {
  firstName: string;
  middleName: string;
  lastName: string;
}
もしStudentクラスにmiddleNameがなかった場合は、コンパイル時にerror TS2420: Class ‘Student’ incorrectly implements interface ‘Person’.
Property ‘middleName’ is missing in type ‘Student’ but required in type ‘Person’.のエラーが表示されます。

しかしmiddleNameを使わない地域によってはmiddleNameはオプションなので必要ではありません。必須ではない(オプション)プロパティに”?”をつけるとStudentクラスでmiddleNameがなくてもエラーになりません。


interface Person {
  firstName: string;
  middleName?: string;
  lastName: string;
}

class Student implements Person {
  firstName: string;
  lastName: string;
}

Classを使ってgreeter.tsを作成する

少し最初は混乱してしまうかもしれませんがClassとInterfaceを利用してgreeter.tsを作成します。

class Studentを作成していますが、constructorでfirstNameとlastNameでpublicを設定しています。

firstNameとlastNameにpublicをつけない場合はエラーになります。

interface Person {
  firstName: string;
  lastName: string;
}

class Student {
  fullName: string;
  constructor(
    public firstName: string,
    public middleInitial: string,
    public lastName: string
  ) {
    this.fullName = firstName + " " + middleInitial + " " + lastName;
  }
}

function greeter(person: Person) {
  return "Hello, " + person.firstName + " " + person.lastName;
}

let user = new Student("Jane", "M.", "User");

document.body.textContent = greeter(user);

index.htmlファイルでgreeter.jsを読み込むと画面にはHello, Jane Userが表示されます。

console.logを利用してStudentクラスから作成したuserオブジェクトの中身を見るとfistNameとlastNameなどを確認することができます。

userオブジェクトの中身
userオブジェクトの中身

シンプルなコードを利用してTypeScriptの型注釈とInterfaceとClassについての基本を確認することができました。