TypeScriptのInterfaceとTypeの違いについて

TypeScriptのInterfaceとTypeどちらも用途は同じような感じで使えてしまい、はっきりとした違いが分からなかったので調べてみました。

InterfaceとTypeについて

Interfaceについて

クラスやオブジェクトの構造を宣言するためのもの

interface MapPoint {
    latitude: string;
    longitude: string;
}

Type(Type Aliac)について

ある型に対する別名をつけるためのもの

type MapPoint = {
    latitude: string;
    longitude: string;
}

InterfaceとType結局どっちを使えばいいの?

公式ドキュメントや色んな記事を参考にしてみた結果、ひとまずこんな感じに結論づけてみました。(後でまた変わるかもしれません)

機能として優れているのはTypeのほうなので基本はInterfaceよりtypeを積極的に使っていけばいいかと思います。
また、プロジェクト全体をオブジェクト思考的に最適化する場合も、Interfaceとtypeのどちらもつかえちゃうが、これもまたtypeのほうを使っていく方向性でいいかと思います。

InterfaceとTypeの違いについて

①用途・目的について

InterfaceType
クラスやオブジェクトの構造を宣言ある型に対する別名をつける

②継承について

InterfaceType
可能可能(交差型)
// interfaceの継承
interface MapPoint {
    latitude: string;
    longitude: string;
}
interface PointPlace extends MapPoint {
    address: string;
}


// typeの継承っぽいやつ(交差型)
type MapPoint = {
    latitude: string;
    longitude: string;
}

type PointPlace = MapPoint & {
    address: string;
}

Classへのimplementについて

InterfaceType
可能可能
// interfaceのimplements
interface MapPoint {
    latitude: string;
    longitude: string;
}

interface PointPlace extends MapPoint {
    address: string;
}

class OfficePlace implements PointPlace {
    latitude: string;
    longitude: string;
    address: string;
    name: string;

    constructor() {
        this.latitude = '';
        this.longitude = '';
        this.address = '';
        this.name = '';
    }
}



// typeのinplements
type MapPoint = {
    latitude: string;
    longitude: string;
}

type PointPlace = MapPoint & {
    address: string;
}

class OfficePlace implements PointPlace {
    latitude: string;
    longitude: string;
    address: string;
    name: string;

    constructor() {
        this.latitude = '';
        this.longitude = '';
        this.address = '';
        this.name = '';
    }
}

let myOffice: OfficePlace = new OfficePlace();
myOffice.latitude = "35.6809591";
myOffice.longitude =  "139.767125";

同名要素のマージ

一度定義したInterfaceの後に再定義を行い、最初のInterfaceに後で追記したInterfaceの型を統合(マージ)することができる。
(個人的にはこれはメリットというよりバグなんかを生むきっかけになりそうな予感)

InterfaceType
可能×
// interfaceの同名要素例
interface MapPoint {
    latitude: string;
    longitude: string;
}

// OK(MapPointで同名定義)
interface MapPoint  {
    address: string;
}

// OK
const placeA: MapPoint  = {latitude: '35.6809591', longitude: '139.767125', address: '東京都'}

// NG
const placeB: MapPoint  = {latitude: '35.6809591', longitude: '139.767125'}
// プロパティ 'address' は型 '{ latitude: string; longitude: string; }' にありませんが、型 'MapPoint' では必須です


// typeの同名要素例
type MapPoint = {
    latitude: string;
    longitude: string;
}

// NG (識別子 'MapPoint' が重複しています)
type MapPoint = {
    address: string;
}

Interfaceだけにある機能

インデックスシグネチャ

オブジェクトを作成する際に、中身のデータ名を抽象的に書きたい場合に使い、プロパティへの添え字アクセスに対する型情報を定義します。

//インデックスシグネチャを定義(keyはstring)
interface StrOptions { [index: string]: string | number; }
let strObj: StrOptions = {};
strObj.name = 'スクリプト太郎';
strObj.age = 100;

//インデックスシグネチャを定義(keyはnumber)
interface NumOptions { [index: number]: string | number; }
let numObj: NumOptions = {};
numObj[0] = 'スクリプト太郎';
numObj[1] = 'スクリプト次郎';
numObj[3] = 'スクリプト三郎';


//typeはエラー
type StrOptions { [index: string]: string | number; }

typeだけにある機能

Mapped Types

Mapped typeは主にユニオン型と組み合わせて使い、インデックス型と同じようにキーの制約として使用することができます。

type Languages = 'en' | 'ja' |'fr' | 'it' | 'es';
type SupportLanguages = { [key in Languages]: boolean; }

// interface はエラー
interface SupportLanguages2 = { [key in Languages]: boolean; }

交差型

type Point = { latitude: string, longitude: string }
type Location = Point & { address: string } 

共用体型

いずれかの型を満たすことができる構造を定義

// numberかstringの型
type Point = number | string
function get(arg: Point) {
  if (typeof arg === 'number') {
    //argがnumberのケース
    console.log(arg);
  } else {
    //argがstringのケース
    console.log(arg.toLowerCase());
  }
}

type内の定義していないプロパティ以外の存在について

declare function getX(obj: { [key: string]: number }): string;
declare function getXX(obj: { price: number, tax: number }): string;

type Price = { price: number, tax: number }
interface IPrice { price: number, tax: number }

const priceA = { price: 100, tax: 10 }
getX(priceA) // OK
getXX(priceA) // OK

const priceB = { price: 100, tax: 10, discount: 20 }
getX(priceB) // OK
getXX(priceB) // OK

const priceC: Price = { price: 100, tax: 10}
getX(priceC) // OK
getXX(priceC) // OK

const priceD: IPrice = { price: 100, tax: 10 }
getX(priceD) // Error!
getXX(priceD) // OK

Leave a Reply

Your email address will not be published. Required fields are marked *