TypeScript

【TypeScript】オブジェクトの型付けを行うインターフェースについて初心者向けに解説

TypeScript

 

今回はオブジェクトの型付けを行うインターフェース(Interface)について解説したいと思います。インターフェースを使うことで、よりスッキリとした可読性の高いコードを書くことができるので、ぜひこの機会にマスターしましょう。

ここではインターフェースの継承についても見ていきます。

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

インターフェースとはオブジェクトの型付けを行うことができる機能です。具体的には、メンバの変数やメソッドなどを一箇所で型付けすることができます。

インターフェースの基本形は以下になります。

interface インターフェイス名 {
    メンバ変数名: メンバ変数の型名;
    メンバ関数名(引数名: 引数の型名): 戻り値の型名;
}

メンバとはクラス内に定義されている変数やメソッドの総称です。

以下のコードでは、変数fruitsnameとsay()メソッドがFruitsクラスのメンバということになります。

class Fruits {
  fruitsname: string;

  constructor(fruitsname: string) {
    this.fruitsname = fruitsname;
  }

  say(): void {
    console.log("私は" + this.fruitsname + "を食べたい");
  }
}

コードで見てみる

インターフェースについて実際のコードでも見ていきましょう。

例えば、2つの言葉を組み合わせるメソッド(mergeWord)が定義されているとします。

function mergeWord(result: {x: string, y: string}) {
  return result.x + result.y;
}

var result = { 
  x: "こんにちは。",
  y: "今日もいい天気ですね。"
}

console.log(mergeWord(result));
=> こんにちは。今日もいい天気ですね。

 

上記のように「xはstring型、yもstring型」というような単純なコードであればそのままでも良いかもしれませんが、もっとコードが複雑になった場合は引数の型付けをまとめて行いたいですよね。

この場合に使えるのがインターフェースという機能です。

前述した通り、インターフェースとはオブジェクトを1箇所にまとめて定義できる機能なので、今回はそれを使ってよりスッキリとしたコードを書いてみましょう。

interface Result {
  x: string,
  y: string
}

function mergeWord(result: Result) {
  return result.x + result.y;
}

var result = { 
  x: "こんにちは。",
  y: "今日もいい天気ですね。"
}

console.log(mergeWord(result));
=> こんにちは。今日もいい天気ですね。
インターフェースを定義する際は「interface ~」と記述する必要があります。

 

このように1箇所に変数をまとめておくことで、可読性が高いコードを書くことができました。これがインターフェースの基本的な使い方となります。

インターフェースの継承

インターフェースもクラスと同様に継承させることができます。

クラスの継承の場合は1つのクラスからしか継承できませんでしたが、インターフェースに関しては複数のインターフェースから継承することができるという点に注意してください。

TypeScript
【TypeScript】クラスの継承とその記述方法について初心者向けにまとめてみたTypeScriptのにおけるクラスの継承とその記述方法について解説しています。クラスの継承をうまく使うことで同じコードをいくつも書く必要がなく新しいクラスを定義することが可能です。クラスの継承はチーム開発をする際に特に重要な概念になってくるので、この機会に理解を深めていきましょう。...

 

ではインターフェースの継承をコードで見ていきましょう。

interface FirstWord {
  x: string;
}

interface SecondWord {
  y: string;
}

interface Sentence extends FirstWord, SecondWord {
  sentence: string;
}

function mergeWord(result: Sentence) {
  return result.x + result.y + result.sentence;
}

var result = { 
  x: "こんにちは。",
  y: "今日もいい天気ですね。",
  sentence: "そう思いますよね?"
}

console.log(mergeWord(result));
=> こんにちは。今日もいい天気ですね。そう思いますよね?
詰め寄るような文言になってしました…

 

コードを分解して詳しく見ていきます。

まずはインターフェースを使用し、xとyの型付けを行なっています。

interface FirstWord {
  x: string;
}

interface SecondWord {
  y: string;
}

 

続いて、先ほどのインターフェース(FirstWordとSecondWord)を継承したSentenceというインターフェースを定義しています。こうすることでSentenceには、xとyとsentenceを持ったインターフェースを作成することができました。

interface FirstWord {
  x: string;
}

interface SecondWord {
  y: string;
}

interface Sentence extends FirstWord, SecondWord {
  sentence: string;
}
インターフェースの継承はクラスの継承と同様にextendsを付与してあげる必要があります。

 

あとはメソッドの引数としてSentenceを定義することで、xとyとsentenceを使用した文言を作成することが可能になります。

function mergeWord(result: Sentence) {
  return result.x + result.y + result.sentence;
}

 

このようにインターフェースの継承を使用することでコードの重複を避けることができ、大規模開発に適したプログラムを書くことができます。

インターフェースのオプション

続いて、インターフェースのオプション化について解説したいと思います。インターフェースのプロパティは、メソッドの引数と同様にオプションにすることができます。

TypeScript
【TypeScript】様々な関数表現について(引数のオプション/アロー関数/オーバーロード/void型)TypeScriptの様々な関数表現について解説しています。基本的にはJavaScriptと同じ表現方法なので、JavaScriptを触ったことがある方はそこまで難しくない内容だと思います。...

 

先ほどのコードのインターフェース(Sentence)をオプション化してみましょう。

interface FirstWord {
  x: string;
}

interface SecondWord {
  y: string;
}

interface Sentence extends FirstWord, SecondWord {
  sentence?: string;
}

function mergeWord(result: Sentence) {
  if (result.sentence) {
    return result.x + result.y + result.sentence;
  } else {
    return result.x + result.y
  }
}

var result = { 
  x: "こんばんわ。",
  y: "今日も寒いですね。",
}

console.log(mergeWord(result));
=> こんばんわ。今日も寒いですね。
クエッションマーク(?)を付与することでオプションにすることができます。
オプション化に加えて、sentenceがあった時とない時で条件分岐をしているね。

インターフェースとクラス

ここまででインターフェースの概要と継承、オプション化について解説しました。最後はインターフェースとクラスについて見ていきましょう。

インターフェースはクラスと組み合わせると、インターフェースの中のプロパティをクラスの方で必ず実装しなければならないという決まりがあります。

以下に基本的なクラス宣言をしたコードを持ってきました。

class Fruits {
  fruitsname: string;

  constructor(fruitsname: string) {
    this.fruitsname = fruitsname;
  }

  say(): void {
    console.log("私は" + this.fruitsname + "を食べたい");
  }
}
TypeScript
【TypeScript】基本となるTypeScriptのクラス宣言について解説TypeScriptのクラス宣言について簡単に解説しています。型を定義するということ以外、基本的にはJavaScriptと変わりませんが改めてみていきましょう。...

 

上記のFruitsクラスに何らかの変数やメソッドを必ず実装させたい場合は、インターフェースを以下のように定義します。

interface DislikeFruits {
  dislikefruits: string;
  saydislikefruits(): void;
}

class Fruits {
  fruitsname: string;

  constructor(fruitsname: string) {
    this.fruitsname = fruitsname;
  }

  say(): void {
    console.log("私は" + this.fruitsname + "を食べたい");
  }
}
今回は嫌いなフルーツに関する変数とメソッドをインターフェースに記述しました。

 

その後、インターフェースを持ったクラスを実装するため「implements DislikeFruits」と記載します。こうすることで、Fruitsクラスは必ず「変数dislikefruits」と「メソッドsaydislikefruits()」を持っていなければならなことになります。

interface DislikeFruits {
  dislikefruits: string;
  saydislikefruits(): void;
}

class Fruits implements DislikeFruits {
  fruitsname: string;

  constructor(fruitsname: string) {
    this.fruitsname = fruitsname;
  }

  say(): void {
    console.log("私は" + this.fruitsname + "を食べたい");
  }
}
「implements」を直訳すると「実装」という意味になります。

 

ここで試しにコンパイルを実行してみましょう。すると、以下のようなエラーが出力され、コンパイルが正常に行われないかと思います。

% tsc test.ts
=> error TS2420: Class 'Fruits' incorrectly implements interface 'DislikeFruits'.

 

このエラーを回避するには「変数dislikefruits」と「メソッドsaydislikefruits()」を実装すれば解消できます。

interface DislikeFruits {
  dislikefruits: string;
  saydislikefruits(): void;
}

class Fruits implements DislikeFruits {
  fruitsname: string;
  dislikefruits: string = "柿";

  constructor(fruitsname: string) {
    this.fruitsname = fruitsname;
  }

  say(): void {
    console.log("私は" + this.fruitsname + "を食べたい");
  }

  saydislikefruits(): void {
    console.log("私は" + this.dislikefruits + "が嫌いだ");
  }
}

 

このように必ず実装しなければならない機能はインターフェースを用いることで実現することができます。

まとめ

  • インターフェースとはオブジェクトの型付けを行うことができる機能。
  • メンバとはクラス内に定義されている変数やメソッドの総称。
  • インターフェースを定義する際は「interface ~」と記述する。
  • インターフェースは複数のインターフェースから継承することができる。
  • インターフェースの継承はクラスの継承と同様にextendsを付与してあげる必要がある。
  • メソッドのプロパティにクエッションマーク(?)を付与することでオプション化にすることができる。
  • インターフェースはクラスと組み合わせると、インターフェースの中のプロパティをクラスの方で必ず実装しなければならなくなる。

参考

インターフェイス 宣言:
https://docs.solab.jp/typescript/interface/declaration/

インターフェース:
https://typescript-jp.gitbook.io/deep-dive/type-system/interfaces

Typescriptのinterfaceの使い方:
https://qiita.com/nogson/items/86b47ee6947f505f6a7b

 

 

今回はオブジェクトの型付けを行うインターフェース(Interface)について解説しました。ぜひインターフェースを使いこなし、大規模開発にも耐えうるプログラムを設計していきましょう。