Open1

TypeScript

chamochamo

TypeScriptでオブジェクトの型に別名を定義する方法には type文 と interface宣言 の2パターンがある。

  • type文(型エイリアス / type alias)
type FooBarObj = {
    foo: number;
    bar: string;
}

const obj: FooBarObj = {
    foo: 123,
    bar: 'Hello, world!'
}
  • interface宣言
interface FooBarObj = {
    foo: number;
    bar: string;
}

const obj: FooBarObj = {
    foo: 123,
    bar: 'Hello, world!'
}

type文 と interface宣言の違い

どちらも同じオブジェクトの型名の定義ができるが、何が違うのか。

1. 代入か宣言か

type文 → 型名を宣言する文。無名で作られた型に参照のため別名を与える。
interface宣言 → 名前が付いた型を宣言する。

2. 定義できる型の種類

type文 → オブジェクト以外の型にも使える
interface宣言 → オブジェクト型にのみ使える

3. オープンエンドと宣言マージ

TypeScript の interface には、オープンエンド(open-ended)と宣言マージ(declaration merging)という珍しい特徴がある。
同じ名前の interface を宣言してもエラーにはならず(オープンエンド)、同じ名前の interface を宣言した場合、それぞれのインターフェースの型がマージされる(宣言マージ)。
この機能を使った拡張した新しい型を生成することができる。type文はこのような拡張はできない。
※type文でも拡張自体はできる(参考

interface User {
  name: string;
}

interface User {
  level: number;
}

const user: User = {
  name: "apple",
  level: 0
};

// 拡張した型を認識してくれるのでエラーが発生
const user2: User = {
  name: "banana"
};  //Property 'level' is missing in type '{ name: string; }' but required in type 'User'

じゃあどっち使うの?

type文 も interface宣言 もどちらもオブジェクトの型を宣言できる、という点では同じであり、type文の方がより多くの場面で使用することができるので、interface宣言 は使わず type文 のみを使うという流儀もあるそう。
2014年以前には type文 は存在せず、interface宣言 のみ利用可能だった、という歴史的背景からオブジェクトには interface宣言 を使う人も多少いるのだとか。

宣言マージを使うタイミングが思いつかず、個人的にはむしろ拡張されたくないなと思ったので、とりあえず type文 を使う方が良さそう。(記事にも本にもとりあえず type文 って書かれてる)

ちなみに

interface宣言 でオープンエンドと宣言マージができる背景としても、JavaScript の歴史によるものが大きいような。

JavaScriptがアップデートされるにつれ、既存のクラスにもメソッドが追加されることがあります。たとえばArrayクラスはES2016でincludes()メソッドが、ES2019でflatMap()メソッドが追加されました。
TypeScriptの開発元は、JavaScriptのアップデートに合わせて、Arrayインターフェースの型定義も対応していく必要があります。単純に考えると、JavaScriptのバージョンごとに、Arrayインターフェースを独立して定義する方法が考えられます。
このアプローチは、一見すると良さそうです。しかし、よく考えてみると、JavaScriptがアップデートされるにつれ、インターフェースのコピペコードが増えていくという問題が出てきます。ES2015とES2016のArrayの違いは、includes()メソッドがあるかないかの違いだけです。それなのに、pop()メソッドやpush()メソッドといった多数のメソッドまでコピーしないといけなくなってしまいます。
これを解決するのが宣言マージです。

参考記事

https://typescriptbook.jp/reference/object-oriented/interface/open-ended-and-declaration-merging

https://zenn.dev/luvmini511/articles/6c6f69481c2d17