[typescript] typeとinterfaceの違いと使い方についてのまとめ

業務でよく見かける「type」と「interface」ですが、実際どう違うのか、とういうときに使えばいいのかがわからず、なんとなく使っている方も多いかと思います。

今回は、この「type」と「interface」についての違いと使い方についてまとめておきます。

\自身のスキルを向上するには技術書で!!/

月額¥980で技術書が読み放題!!

  • ビジネススキルとマインド向上したい!!
  • 決断や行動を先送りにしてしまう方!!

Kindle Unlimitedでは30日間無料体験実施中!登録はこちら!

type

type(正しくはAype Aliases)とは、ある「型」に対して別の名前をつける場合に利用します。

// example 1
type exampleType = string;
let exam: exampleType = "hello world";
console.log(exam);
// hello world

// example 2
type exampleType2 = {
    id: number,
    name: string,
}

let exam2: exampleType2 = {
    id: 1,
    name: "kunisura"
}
console.log(exam2.name);
// kunisura

example 1の方では「string」という型に対して「exampleType」という名前をつけてあげて、変数を定義するときに、typeで定義した型を指定しています。

example 2の方では「オブジェクト」の方を「exampleType2」という名前で型を定義してあげて、実際に変数を作成するときは、exampleType2の型を定義して、値を設定します。

interface

interfaceもtypeと同様に型を宣言します。実際typeとinterfaceは使い方によっては区別がつきません。(書き方のみ)後ほど、違いや使い方についてはまとめます。

interface IExampleType {
  name: string;
}

let exam3: IExampleType = {
  name: "kunisura"
}
console.log(exam3.name);
// kunisura

interfaceはオブジェクト・クラスの構造を決め、そのオブジェクトクラスの型に名前をつけるときに利用します。

typeとinterfaceの違い

typeもinterfaceも型に名前をつけるという点では同じですが、それぞれが持つ意味は全く異なります。

interface は「宣言マージ」が可能

interfaceを利用する場合の大きな特徴として「宣言マージ(declaration merging)という特徴があります。
PHPなどでは、同じインターフェースを宣言すると「エラー」となりますが、TypeScriptでは同じ名前のinterfaceを宣言してもエラーとはなりません。

これは大きな特徴で、TypeScriptでは同じ名前のinterfaceをマージしてくれます。

interface IExampleType2 {
  name: string;
}

interface IExampleType2 {
  id: number;
}

let exam4: IExampleType2 = {
  name: "kunisura",
  id: 1
}
console.log(exam4.name);

上記は、下記のように定義したものと同じものになります。

interface IExampleType2 {
  name: string;
  id: number;
}

ただ、typeでは上記のようにマージはできません。しかしinterface型に対してtype型をマージすることは可能です。
その場合は、型名が変わるので、結局は別の型を定義しているのと変わらないかと思います。

interface IExampleType3 {
    id: number;
}

type exampleType3 = {
    name: string,
}

type margeExampleType = IExampleType3 & exampleType3;
const exam5: margeExampleType = {
  id: 1,
  name: "kunisura"
}
console.log(exam5.name);

interfaceは拡張が可能

interfaceの特徴として、「extends」を利用して別で定義したinterfaceやtypeを拡張することができます。

interface IExampleType3 {
    id: number;
}

type exampleType3 = {
    name: string,
}

interface IExtendExampleType extends IExampleType3, exampleType3 {
  message: string;
}

const exam6: IExtendExampleType = {
  id: 1,
  name: "kunisura",
  message: "kunisura is happy."
}
//{ id: 1, name: 'kunisura', message: 'kunisura is happy.' }

定義形式がinterfaceの場合は固定

interfaceの場合は、下記のような形式でのみ可能となります。(propertyNameは省略可能)

interface INTERFACE_NAME {
    propertyName: TYPE
}

しかし、typeの場合は、型に別名をつけるだけなので、色々な型に対して定義することが可能です(下記は一例です)

// Object
type TypeName1 = {
    name: string
}

// string
type TypeName2 = string

// Map
type TypeName3 = { [P in keyof TBase]: string; }

// Function
type TypeName4 = (message: string) => void;

// Tuple
type TypeName5 = [string, number];

typeは「typeof」を使って型を認識できる

typeの場合は、いちいち宣言しなくても、すでに作成済みのオブジェクトの型をtypeofを使って認識することができます。

const engine = { capacity: 2200, type: "diesel" }
// 型をtypeofを使って認識
type Engine = typeof engine;

const customEngine: Engine = { capacity: 2500, type: 'petrol'};
console.log(customEngine);
// { capacity: 2500, type: 'petrol' }

実際どっちを使えば良いのか?

結果として、私の考えとしては「どちらでも良い」というのが正直なところです。基本的にはプロジェクトに合わせて利用するのが良いかと思います。

コーディングルールなどがあるプロジェクトもあるので、そのルールに則るのが良いかと思います。

また、「宣言マージ」を利用する必要があるかどうかで決めても良いかと思います。

ただ、バックエンド言語(C#やJavaなど)に慣れている方は、下記のような感じで使い分けるのもアリかと思います。

  • メソッドを定義してclassで実装する場合:interface
  • データを格納するためのオブジェクトで利用:type

まとめ

以下、簡単にまとめてみました。

項目typeinterface
型定義可能可能
同じ名前で定義不可可能(declaration merging)
自動型認識typeof利用不可
記述形式様々な型に対して定義可能記述形式固定
extendsextends利用不可extends利用可能
implements利用可能利用可能(ただし、union typeなどは不可)

最後に

今回はtypescriptでのinterfaceとtypeの違いと使い方についてまとめました。

意外と区別されているということを理解できるかと思います。是非、理解して利用してみるとまた違ったコーディングができると思います。

タイトルとURLをコピーしました