━目次━
最近ReactをレベルアップさせようとTypeScriptを使い始めました。
そこで何やらstateやpropsの型を定義するのにinterfaceを使う方法とtypeを使う方法があることが分かり、
「どっちがええね~ん」
ってなったので調べたら非常に丁寧にまとめてある記事があったので翻訳してみました。
Martin HochelというGoogle Developer Expertもされてる方のようですね。
上記の記事には2019年6月の時点で7200イイネついてます。
この翻訳記事を作成するにあたり、本人にTwitterから連絡をとってみました。
This article is very clear to understand about TS.
May I translate it and introduce on my blog for many Japanese engineers?https://t.co/jNz4r3CaWQ
— フリーランス-じゃけぇ-@ノーマウント勉強会主催 (@bb_ja_k) June 15, 2019
返事ないかなーと思ってましたが、以下のように返事が頂けました!
Hey! glad you like it. About that translation, sure why not. Just make sure to give me copyright credits, also ping me when it's ready I'll reference it in original. Arigato Gozaimasu
— Martin Hochel (@martin_hotell) June 17, 2019
どうやら元記事にリンクも載せてくれるようです( ;∀;)

なんて良い人なんだ~
英語強い訳でもなければTypeScriptも始めたばかりなので、間違いあれば指摘して頂けると助かります。
個人的に理解し易いのでかなり口語的に意訳してるので悪しからず…
Interface vs Type alias in TypeScript 2.7
「TypeScriptでコンパイル時の型を定義するためにtypeとinterfaceを使うことの違いは何でしょうか」という質問しょっちゅうされます。(オンラインとか職場とかスケートパークで(;’∀’)w)
まずは、「とりあえずTypeScriptハンドブック読んでみー」って言ったのですが…
残念ながら、ほとんど皆求めている答えを見つけることができませんでした(Advanced Typesセクションに隠されています)。
仮に見つけたとしても、情報が古いんですよね(書かれてる挙動はtypescript@2.0.xのため)。
そんな皆さんに朗報ですよー!
この記事は、interfaceとtype aliasの使い方に関する最新の説明/スタイルガイドです。
公式のドキュメントに書いてあること
type aliasは一種のinterfaceのように振る舞うことができますが、わずかな違いがいくつかあります。
これは合ってますわ!
では、何が違うのか?
1. 1つの違いは、interfaceはどこでも使用される新しい名前を作成するということです。 type aliasは新しい名前は作成しません。たとえば、エラーメッセージにalias名は使用されません。
これは間違ってますわ!(TypeScript 2.1から)
interfaceとtype alias、およびパラメータ型注釈にinterfaceとtype aliasの両方を使用するgetRectangleSquare関数の2つの実装を介して、Pointのコンパイル時の型を定義してみましょう。
interfaceとtypeで定義されたPoint
interfaceとtype aliasを使うgetRectangleAreaファンクション
interfaceとtype alias両方に対する同じエラー
どっちも同じエラー起きてますね:
// Interface
型 ‘{ x: number; }’ の引数を型 ‘PointInterface’ のパラメーターに割り当てることはできません。
// Type alias
型 ‘{ x: number; }’ の引数を型 ‘PointType’ のパラメーターに割り当てることはできません。
2. 2番目に重要な違いは、type aliasの拡張や実装ができないことです。
これまた間違ってますわ!
type aliasでinterfaceを拡張することができるからね。
type aliasで拡張されたinterface
もしくはClass制約を実装するためにtype aliasを使います。
type aliasで拡張されたclass
もしくはClass制約を実装するためにtype aliasによって拡張されたinterfaceを使います。
ThreeDimensionはPointTypeを拡張してます。 PointTypeはtype alias
クラス制約を実装するためにtype aliasとinterfaceの両方を組み合わせることもできます。
type aliasとinterfaceの組み合わせで実装されたクラス
3.type aliasは他の型を拡張/実装できません」
これまた間違ってますわ!
合ってるっちゃ合ってるけど、紛らわしい表現ですね。( – -)
交差演算子「&」を介したtype aliasの拡張には、interfaceまたは他のTypeScript有効型(DictionaryとかObject、非プリミティブ型等々)を使用できます。
交差演算子を使って作成されたtype aliasを実装するクラス
また、マッピングされた型をinterfaceとtype aliasの両方のさまざまな変換に活用することもできます。
PartialのマップにShapeとPerimeterを用いてみましょう
クラスは交差とマップされた型で作成されたtype aliasを実装しています。
perimeter()とarea()はオプションなので、これらをクラス内で実装する必要はありません。
weak type検出も正しく機能します。
weak type検出が期待通りに機能している
type aliasとinterfaceの両方を持つハイブリッド型
場合によっては、関数とオブジェクトの両方として機能するオブジェクトを、追加のプロパティーと共に定義したいことがあるでしょう。
ここで言ってるのは、関数の型(呼び出し可能オブジェクト)とその関数の静的プロパティの定義のことね。
このパターンは、サードパーティのJavaScriptを扱うときに型の形状を完全に記述するのによく見られます。

ハイブリッド型の定義と実装
type aliasでのハイブリッド型
ただビミョーに違いがあります。
Countertypeへの参照の代わりにIDEで特別な形状のタイプが見れます。
CodeSandboxでは上手くいかなかったので元のgifを見てください
(https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c#1a94)
実践するのにオススメは、ハイブリッドな定義を2つの部分に分けて分析することです。
・呼び出し可能オブジェクト(関数)type alias
interfaceとtype aliasの違いについての再考
1.型定義内でunion演算子を使用する場合、type aliasを持つクラスでimplementsを使用することができない
違い1:type aliasにunionを使う場合implementsは使えない
これは全くもって理にかなっています!
クラスは、いくつかの形状構造をimplementsすることはできませんので、何ら驚くべきことではないですよね。
type aliasにunionを使う場合で上手いこと動かそうと思ったら、オブジェクトリテラルでのオブジェクト定義を用います。
以下の例では、perimeter()またはarea()メソッドのどちらかあるいはその両方を定義しなければならないので、どちらもない場合はコンパイルエラーが起きるのが分かります。
type aliasのunion:オブジェクトリテラルによる正しい使用法
2.型定義内でunionを使用している場合、type aliasを持つinterfaceではextendsを使用できない
違い2:type aliasのunionはinterfaceのextendsには使えない
繰り返しになりますが、クラスのimplementsの使用法と同じく、interfaceは静的です – それはいくつかの形で存在することはできないので、unionされた型定義でextendsできないのです。
3.宣言のマージはtype aliasでは機能しない
宣言のマージはinterfaceでは機能しますが、type aliasでは失敗します。
宣言マージとは何かと言うと、
同じinterfaceを複数回定義でき、その定義を1つにまとめることができる機能のことです。
宣言のマージ
typeは一意の型エンティティであるため(グローバル、モジュールスコープの両方で)、これはtype aliasでは機能しません。
違い3:type aliasは宣言のマージをサポートしない
TypeScriptで作成されていないライブラリに対してサードパーティのアンビエントタイプ定義を作成している場合は、interfaceを介した宣言のマージは非常に重要です。
ライブラリがTypeScriptで書かれていてアンビエントタイプ定義が自動的に生成される場合も同様です。
これはほんの一例ですが、type aliasの代わりに必ずinterfaceを使うべきですよ!
ReactのProps and Stateには何を使うべき??
一般的には、あなたが望むほう(type alias/interface)を使用してください。ただ個人的には、type aliasesを使用することをお勧めします。
typeで書いたほうが短い
一貫した記述にする(interfaceとtype aliasを混ぜない)
1 2 3 4 5 6 7 8 |
// BAD interface Props extends OwnProps, InjectedProps, StoreProps {} type OwnProps = {...} type StoreProps = {...} // GOOD type Props = OwnProps & InjectedProps & StoreProps type OwnProps = {...} type StoreProps = {...} |
・あなたの公開コンポーネントのProps/Stateを実装する際には、モンキーパッチを適用することはできません。そのため、コンポーネントの使い手はinterface宣言のマージを利用する必要はないのです。拡張のためにはHOCなどのように明確に定義されたパターンがあります。
要約
この記事では、最新のTypeScriptにおけるinterfaceとtype aliasの違いについて学習しました。
その結果、どういったシナリオでどちらを使用するべきかという結論に達しました。
type aliasはinterfaceのように振る舞うことができるが、3つの重要な違いがある(union、宣言マージ)
チームに合ったものを統一的に使用する
ライブラリまたはサードパーティのアンビエントタイプの定義を作成するときは、常にパブリックAPIの定義にinterfaceを使用する
React ComponentのPriopsとStateにはtypeを使用することを検討する
さいごに
ということでした!
私としてはReactのPropsとStateにはとりあえずtype使っとけってことでしたのでその方向でいこうと思います。
unionとか宣言マージとか複雑なことをしようとすると違いがでるようですが、なるべくそうならないようなつくりにするほうが大事なんでしょうね~と思ったり。
最初にも書きましたが、翻訳が至らない部分もあるかと思いますので、ご指摘等あればコメントいただけると助かりますm(_ _)m
それでは!また!!
人気の記事だけ集めたので是非覗いていってください^^
厳選!目的別にオススメ記事を紹介-あなたの欲しい情報がここに-