TypeScriptでType AliasesとInterfacesどちらを使うべきか
散々話題になってる気がするけど。
- TypeScript: Documentation - Everyday Types > Differences Between Type Aliases and Interfaces
-
Google TypeScript Style Guide
- "Interfaces vs Type Aliases" という項がある
- TypeScript: Prefer Interfaces — @ncjamieson
- interfaceとtypeの違い、そして何を使うべきかについて
- TypeScript の Interface と Type Alias の違い - Qiita
- Types vs. interfaces in TypeScript - LogRocket Blog
-
Tidy TypeScript: Prefer type aliases over interfaces
- これはtype推しだ
- TypeScript使いに質問です。InterfaceとTypeの使い分けはどうしていますか?どっちか一方だけに統一していますか?また有用なリンクがありますか? - Quora
- Interface vs Type alias in TypeScript 2.7 | by Martin Hochel | Medium
知りたいこと
- 概念的な違い
- 挙動の違い
- どちらがオススメかと、その理由
interfaceとtypeの違い、そして何を使うべきかについて
「3. どちらを使う?」ではどちらかに決定してるわけではなく、双方の言い分をまとめてる。
interface 派
始めの部分で申し上げた通りTSに関する記事を探したらinterfaceを使うようにと言ってるところも多いです。そしてその根拠としては主に拡張性をあげています。公式ドキュメントでもinterfaceのほうが拡張にオープンなJavascriptのオブジェクト動作方式に似ているという理由でinterfaceをおすすめしています。
type 派
- interface で定義できるのはオブジェクトとクラスだけだが、 type は他の型でも使える (string literal typesとか)
- interface は2回定義すると拡張できる(言い換えると、知らないうちに拡張される可能性がある)
- interface でできることはだいたい type でもできるので type でいい
Google TypeScript Style Guide
when declaring types for objects, use interfaces instead of a type alias for the object literal expression.
というわけでinterface推し。
Why?
These forms are nearly equivalent, so under the principle of just choosing one out of two forms to prevent variation, we should choose one. Additionally, there also interesting technical reasons to prefer interface. That page quotes the TypeScript team lead: Honestly, my take is that it should really just be interfaces for anything that they can model. There is no benefit to type aliases when there are so many issues around display/perf.
で、ここで参照してるのが TypeScript: Prefer Interfaces — @ncjamieson という記事。
これはたしか type 使うと生成される .d.ts のサイズがでかくなる可能性がある、みたいな話だったはず。
TypeScript: Documentation - Everyday Types > Differences Between Type Aliases and Interfaces
冒頭で
Type aliases and interfaces are very similar, and in many cases you can choose between them freely
と書いてるので多くのケースではどちらでもよい
Almost all features of an
interface
are available intype
, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.
type は一度定義されたものはre-open(再定義、の意味?)して新しいプロパティを追加できない一方、
interface はいつでもextendable。
- 型をextendするときは
- interface:
extends
を使う - type: intersection type (
&
)を使う
- interface:
- interface は二重定義によりフィールドを足すことが可能だが、type はエラー
がサンプルコードつきで載ってる。
You’ll learn more about these concepts in later chapters, so don’t worry if you don’t understand all of these right away.
の後ろにinterfaceとtypeの挙動の違いサマリ的な箇条書きがあり
-
TS 4.2 以前は、type aliases に変換をかませたとき(
Omit
とか)に、エラーメッセージでエイリアス名が表示されなかった
-
Type aliases は同じ定義が複数あるとマージしてくれずエラー(↑に書いてる話)
-
Interfaces はオブジェクトの構造を宣言するのに使えるが、プリミティブな値に名前をつける用途は不可
-
Interfaces 名はエラーメッセージで必ず表示されるが、名前を指定して使われているときのみ
- これは サンプルコード 見るのが早い
結論としては
For the most part, you can choose based on personal preference, and TypeScript will tell you if it needs something to be the other kind of declaration. If you would like a heuristic, use interface until you need to use features from type.
Types vs. interfaces in TypeScript - LogRocket Blog
Type Aliases と Interfaces の違いとして挙げてるのは、
- interface は Declaration Merging が可能、type はNG
- interface は class から extends できるが type はNG
- class が implements できるのはどちらもOK
- 複数の interface を
&
で組み合わせて交差型(結果は type)が作れるが、逆(複数の type から interfaceを作る)は不可 - 複数の interface を
|
で組み合わせてUnion(結果は type)が作れるが、逆(複数の type から interfaceを作る)は不可 - タプルは type のみ
What should I use?
Interfaces are better when you need to define a new object or method of an object. For example, in React applications, when you need to define the props that a specific component is going to receive, it’s ideal to use interface over types:
Types are better when you need to create functions, for example. Let’s imagine that we have a function that’s going to return an object called, type alias is more recommended for this approach:
type Person = {
name: string,
age: number
};
type ReturnPerson = (
person: Person
) => Person;
const returnPerson: ReturnPerson = (person) => {
return person;
};
後者の、これがtypeが適している理由がよくわからない。。。
また、最後に
You should not start to use one and delete the other. Instead of doing that, start to refactor slowly, thinking of what makes more sense to that specific situation.
こう書いていて、そうだよね結局どちらかに一意に決められるものではないよねという。
交差型についてはTypeScript 公式ドキュメント Interfaces vs. Intersections
We just looked at two ways to combine types which are similar, but are actually subtly different. With interfaces, we could use an extends clause to extend from other types, and we were able to do something similar with intersections and name the result with a type alias. The principle difference between the two is how conflicts are handled, and that difference is typically one of the main reasons why you’d pick one over the other between an interface and a type alias of an intersection type.
Tidy TypeScript: Prefer type aliases over interfaces
共通点
- class の
implements
で使える - オブジェクトリテラルの型アノテーションで使える
- 再帰的な型構造 (recursive type structures) に使える
Declaration merging
- TS本体では lib.d.ts 内でこれを多用してるよ
-
FormData
(DOM APIに定義されてる)みたいなinterface名を使ってしまうと、知らないうちにマージされて本来ないはずのプロパティにアクセスするコード書いてもコンパイル通るみたいなこと起きる
Index access types
たとえば Record<string, string> = { [key: string]: string }
などは index access types。
こういった型に対して type は渡せるが interface は渡せない。
これは、先ほどのdeclaration mergingという機能により、interface は宣言時点ではまだすべてのプロパティが既知ではないので、
こういった型に対しては渡せなくなる。
TypeScript使いに質問です。InterfaceとTypeの使い分けはどうしていますか?どっちか一方だけに統一していますか?また有用なリンクがありますか? - Quora
判断の立脚点となるのはinterfaceが何であって、今宣言しようとしているその型は本質的にinterfaceなのかどうかです。
パーシャルクラスはなぜ必要か
ライブラリでinterfaceパーシャルクラスが有用になるケースというのは、JSとの兼ね合いで、たとえばwindow.*のグローバル変数の宣言のケースがあります。そういう形で拡張をしていかないなら、最初からTypeScriptだけでコーディングをしているならパーシャルクラスが有用なことはほとんどないでしょう。たとえばReactのPropsでアドホックに拡張が必要になるケースはほとんどないはずです。交差型でもいいはずですしね。
ということから、ライブラリであっても独自の型定義でユーザーが同名で拡張するような使われ方を想定してないのであればtypeでいいのでは感。
TypeScript: Prefer Interfaces — @ncjamieson
これは以前も読んだ記事。
冒頭に
Some time after this blog post was written, Anders Hejlsberg opened a PR that preserves type aliases for union and intersection types. That PR’s changes should included in TypeScript 4.2, so when that version is released, the reasons for preferring interfaces might be less compelling.
て追記されてた。
が、記事中のIIFEの例は改善されてないように見えた(Playground)。
記事の主張
type alias使うと、生成される.d.ts内でエイリアスがインライン展開されるケースがあって、その場合interfaceと比べて.d.tsのサイズが肥大化するよという話。
const read = (() => {
type ReadCallback = (content: string) => string;
return function (path: string, callback: ReadCallback) {};
})();
は
// .d.ts
declare const read: (path: string, callback: (content: string) => string) => void;
となる。
一方interfaceはコンパイルエラーでそもそもこういう書き方はできない。
Exported variable 'read' has or is using private name 'ReadCallback'.
Interface vs Type alias in TypeScript 2.7 | by Martin Hochel | Medium
記事が2018年5月。TS 2.7 の情報なので古い部分あるかも。
So what’s the difference between type alias and interface again 🤖?
-
you cannot use
implements
on an class with type alias if you useunion
operator within your type definition -
you cannot use
extends
on an interface with type alias if you useunion
operator within your type definition -
declaration merging doesn’t work with type alias
-
union types 使った type alias は class に implement できないよ
- 同様に、union types 使った type alias は interace に extends できないよ
- 宣言のマージはtype aliasだとできないよ
⚛️: What should I use for React Props and State ?
In general, use what you want ( type alias / interface ) just be consistent, but personally, I recommend to use type aliases:
-
type
のほうが宣言が短く書ける - "your syntax is consistent ( you are not mixin interfaces with type aliases for possible type intersections )"
// BAD
interface Props extends OwnProps, InjectedProps, StoreProps {}
type OwnProps = {...}
type StoreProps = {...}
// GOOD
type Props = OwnProps & InjectedProps & StoreProps
type OwnProps = {...}
type StoreProps = {...}
→これは、別に全部 interface に統一すれば一貫性あるのでは?? 🤔
typescript-cheatsheets/react: Cheatsheets for experienced React developers getting started with TypeScript
https://github.com/typescript-cheatsheets/react#types-or-interfaces にtypeとinterfaceの比較あり
TL; DR; 的には
Use Interface until You Need Type - orta.
というわけでinterface使っとけ、なんだけど、その後に
consider using type for your React Component Props and State, for consistency and because it is more constrained.
とかも言ってるので主張がよくわからないことになってる
Preferring Interfaces Over Intersections
公式Wiki。
interfaceを推奨する理由をこのへんでいろいろ挙げてるけど理解できてない。
Interfaces create a single flat object type that detects property conflicts, which are usually important to resolve! Intersections on the other hand just recursively merge properties, and in some cases produce never. Interfaces also display consistently better, whereas type aliases to intersections can't be displayed in part of other intersections. Type relationships between interfaces are also cached, as opposed to intersection types as a whole. A final noteworthy difference is that when checking against a target intersection type, every constituent is checked before checking against the "effective"/"flattened" type.
プロパティが衝突したとき、interfaceではエラーにしてくれるけどtype aliasはしてくれないという挙動の違いを忘れてた。
type aliasだと
type Foo = {
x: number
}
type Bar = {
x: string
}
type Baz = Foo & Bar // x: never になる
const baz: Baz = {}