Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

type エイリアスと interface の使い分けについて #790

Open
version-1 opened this issue Nov 3, 2023 · 6 comments
Open

type エイリアスと interface の使い分けについて #790

version-1 opened this issue Nov 3, 2023 · 6 comments
Labels
stage1:話し合い中 結論を出すために話し合いをしている最中です。まだ着手・プルリクエストの作成はしないでください。

Comments

@version-1
Copy link

質問内容

type エイリアスと interface の使い分けについて

https://typescriptbook.jp/reference/object-oriented/interface/interface-vs-type-alias#%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9%E3%81%A8%E5%9E%8B%E3%82%A8%E3%82%A4%E3%83%AA%E3%82%A2%E3%82%B9%E3%81%AE%E4%BD%BF%E3%81%84%E5%88%86%E3%81%91

リンクの箇所に

実際に型を定義する時にインターフェースと型エイリアスのどちらを使うのがよいのでしょうか?残念ながら、これに関しては明確な正解はありません。

といった内容がありますが、TypeScript から離れて一般的に インターフェースと型の概念自体は異なっており、TypeScript でも同様で他の言語と同様に型とインターフェースを使い分けるべきかと思うのですがいかがでしょうか?

自身の理解の確認の意味もございますので、詳細ご確認いただき返答頂けると幸いです。

詳細

Java, Go, PHP 等を念頭においたインターフェースの概念自体、私の理解ではダックタイピングを念頭においており、「使う側から最低限これだけのプロパティ・メソッドが実装されていれば良い」というのを示すものだと考えています。

一方、型自体はより具象的な型を示すためインタフェースを使う際のユースケースとは違い「まさにこのモジュールを動かすにはその具象的な型の全てのプロパティや型が必要」といったことを示す違いがあり、使い分けについては上記に書いた Java, Go, PHP と同様に使い分けられるべきだと考えています。

また、少し古いドキュメントにはなるかと思いますが、「TypeScript における interface とは何か?」の説明として、個人的に一番しっくり来るのがこちらのドキュメントの冒頭の記述になります。

https://www.typescriptlang.org/docs/handbook/interfaces.html

One of TypeScript’s core principles is that type checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural subtyping”. In TypeScript, interfaces fill the role of naming these types, and are a powerful way of defining contracts within your code as well as contracts with code outside of your project.

以上を踏まえて、型とインタフェースの使い分けの大枠として、その「ほかの言語にある型とインタフェースと同じ使い分けをする」といった説明で良いのかと思います。(実際のドキュメントではそれぞれの違いを説明する必要もあるかとは思いますけど)

補足

プルリクで編集の提案をしようかとも思ったのですが、注意書きにもあるようにあらかじめ合意を得る必要がありそうなのと自分の理解を確認させていただきたかったので issue を立たさせていただきました。

上記自分の理解に抜け漏れなどなければ、編集プルリクエストも送らせていただきたいと考えております。

こちらのドキュメントも含めていて、typeエイリアス で interface で「ぞれぞれできること」は示しているのですが「そもそもの型とインタフェースの違い」についてはあまり書かれていない印象なのでその辺り改善できればと思っています(本ドキュメントでの言及はありますがあらかじめ使い分けやそれぞれの概念の違いに触れてからの方が「できること」の内容もハラオチしやすいように思っております。)
https://www.typescriptlang.org/docs/

@suin
Copy link
Contributor

suin commented Nov 3, 2023

@version-1 「ほかの言語にある型とインタフェースと同じ使い分けをする」というのは具体的にどのような使い分けをイメージされているのでしょうか?例に挙げてくださったプログラミング言語Java、PHP、Goなどで型エイリアスとインターフェースがあり、それらの使い分けについて示したドキュメントもありましたら共有いただけると議論の材料になると思います。

@suin suin added the stage1:話し合い中 結論を出すために話し合いをしている最中です。まだ着手・プルリクエストの作成はしないでください。 label Nov 3, 2023
@version-1
Copy link
Author

「ほかの言語にある型とインタフェースと同じ使い分けをする」というのは具体的にどのような使い分けをイメージされているのでしょうか?

勘違いしていたところあり、 type エイリアスは具象型に近いものかと思っていたのですが、インターフェースなどの 抽象型に近いと理解しました。

https://www.typescriptlang.org/docs/handbook/advanced-types.html#interfaces-vs-type-aliases

As we mentioned, type aliases can act sort of like interfaces;

「要求されたプロパティが実装されれば動く」というのが interface だけの機能かと思っていたのですが、下記のようなケースで type エイリアスでも問題なく動作するとわかったのでインターフェースに近いと考えました。

type Props = { a: number }
type MoreProps = { a: number; b: number }

const hoge = (props: Props) => console.log(props)
const obj: MoreProps = { a: 1, b: 2 }

hoge(obj)

これを踏まえて議論の論点を訂正させていただければと思います。

@version-1
Copy link
Author

version-1 commented Nov 4, 2023

具体的な使い分けについて(訂正後)

1. それぞれの使い分けの結論に対する記述について

実際に型を定義する時にインターフェースと型エイリアスのどちらを使うのがよいのでしょうか?残念ながら、これに関しては明確な正解はありません。

といった内容がありますが、ドキュメントを読む限り公式等も「型エイリアスがどうしても必要な場合(Union Type や primitve な型にエイリアスをつけたい場合など)をのぞいて interface を使うのが望ましい」のではないかと思っており、本ドキュメントもそのように修正を検討した方が良いと思うのですがどうでしょうか?

本ドキュメントで触れている Google のスタイルガイド でも、
https://google.github.io/styleguide/tsguide.html#interfaces-vs-type-aliases

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.

となっている様ですし、現TypeScritptのドキュメントでも同様の項目 Differences Between Type Aliases and Interfaces
https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces

If you would like a heuristic, use interface until you need to use features from type.

となっている様なので、特段理由がなければ interface を使っていれば問題ないといった姿勢になっているように見えます。

2. 同項目使いわけのルールを統一する際についての記述

インターフェースと型エイリアスの使い分けに悩む場面が多く開発スピードが落ちてしまうのであれば、型エイリアスに統一して書く方針にする考え方もあります。

上記と関連しますが、 interface 側に倒す様なある中で型エイリアス側に倒す例を挙げているのが読んでいて腑に落ちない点がありました。本ドキュメントがある程度TypeScriptエントリーレベルの読者を想定しているのであれば、「型エイリアスがどうしても必要な場合(Union Type や primitve な型にエイリアスをつけたい場合など)をのぞいて interface を使うのが望ましい」という姿勢を勧めるのが優しい様な気がします。

3. 該当のページの構成について

こちらは色々な意見や他ページとの平仄の問題などあるかと思いますが、
現状は、

a. 違いを説明
b. 使い分けを説明

のような構成になっているものを

a. 使い分けを説明
b. 違いを説明

反転させることを検討しても良いのではと思います。

後者のメリットとしては、例えば

  • 読者が a. を読んで inteface をベースで使っていくと理解した場合にある程度 type エイリアスを捨て
    置いて学習を進める。
  • interface が多く使われるということが分かった上である程度、重要度を理解しながら本ページを読み進めることができる。

といったものが考えられます。

これは主観的かもしれませんが「TypeScript 違い」として検索した場合に違い自体を知りたいというよりその「使い分け」を知りたい場合が多いかと思うので構成を見直しても良いのかなと思っております。

@suin
Copy link
Contributor

suin commented Nov 10, 2023

@version-1

2. 同項目使いわけのルールを統一する際についての記述

インターフェースと型エイリアスの使い分けに悩む場面が多く開発スピードが落ちてしまうのであれば、型エイリアスに統一して書く方針にする考え方もあります。

上記と関連しますが、 interface 側に倒す様なある中で型エイリアス側に倒す例を挙げているのが読んでいて腑に落ちない点がありました。本ドキュメントがある程度TypeScriptエントリーレベルの読者を想定しているのであれば、「型エイリアスがどうしても必要な場合(Union Type や primitve な型にエイリアスをつけたい場合など)をのぞいて interface を使うのが望ましい」という姿勢を勧めるのが優しい様な気がします。

これについてもう少し詳しく教えて頂きたいです!

  1. なぜそのほうが初心者に優しいと思うのか?
  2. もし下記を書き直すとしたら、どういう表現にしたいのか?

インターフェースと型エイリアスの使い分けに悩む場面が多く開発スピードが落ちてしまうのであれば、型エイリアスに統一して書く方針にする考え方もあります。

@version-1
Copy link
Author

お忙しい中対応ありがとうございます。

1. なぜそのほうが初心者に優しいと思うのか?

色々と調べていく中で、interface側に倒す方が良いというような記事等に触れることが多く、初学者がわざわざそれに反する選択をする必要はないと考えたからです。

記事中で引用しているスタイルガイドでも、 interface 側に倒すことが紹介されていたり(前コメントで私が引用した部分)、スタイルガイド内で紹介されている記事 でも

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.

We tried for a long time to paper over the distinction because of people’s personal choices, but ultimately unless we actually simplify the types internally (could happen) they’re not really the same, and interfaces behave better.

といった引用があります。「type エイリアスしか適用できないケース以外は interface を使うべき」といった主張が多く見受けられるようですし、技術的に両方適用できる場面では interface 側にメリットがあると書かれているように思います。(最新のバージョンで改善されている等、見落としている部分があれば別ですが・・)

こういった主張を検討せずに初学者が type エイリアスを使う方に統一する理由がイマイチ見当たらないと考えます。

2. もし下記を書き直すとしたら、どういう表現にしたいのか?

冒頭で両者の使い分けに答えはないと言っているので、両方の立場を書いたのかと推測しているのですが、前段で上記のような interface 採用のメリットをサポートしておいて急に

インターフェースと型エイリアスの使い分けに悩む場面が多く開発スピードが落ちてしまうのであれば、型エイリアスに統一して書く方針にする考え方もあります。

となっていたので、当記事だけ見ると型エイリアスを使う理由が判然としないと感じました。

個人的な意見としては、上に書いたような interface 優勢な状況があるので type エイリアスと interface の使い分け のセクションで

  1. (結論) 型エイリアスがどうしても必要な場合(Union Type や primitve な型にエイリアスをつけたい場合など)をのぞいて interface を使うのが望ましい。
  2. (サポート) 上記で説明したスタイルガイドの引用や引用記事で触れられている技術的メリット
  3. (まとめ)ひとまずは interface を使ってみてそれで間に合わない場合 type エイリアスを使うとすると良い

といったような構成になると良いと思っております。

現状のように、両方の立場を紹介する場合は、型エイリアスを使う根拠を紹介したり「一方」などのワードを使って対立を明確にすると読みやすくなるかとも思っています。

@t-yng
Copy link
Contributor

t-yng commented Nov 24, 2023

@version-1
対象の記事の部分を書いた者です。
ご指摘ありがとうございます!!

こちらは、執筆する時にも記述をどうするか非常に悩んでおりました。

@version-1 さんのご主張も最もだと思っておりまして、どちらが良いという話のコメントではないですが、自分が今の記述をした背景を議論の参考に書かせて頂きます。🙆🏻‍♂️

「type エイリアスしか適用できないケース以外は interface を使うべき」といった主張が多く見受けられるようですし、技術的に両方適用できる場面では interface 側にメリットがあると書かれているように思います。(最新のバージョンで改善されている等、見落としている部分があれば別ですが・・)

こういった主張を検討せずに初学者が type エイリアスを使う方に統一する理由がイマイチ見当たらないと考えます。

Googleスタイルガイドやその他の記事などでもインタフェースを使うべきという主張は多く見受けられるのですが、最新のTypeScriptの公式ドキュメントでは

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 in type, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.

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.

と書かれており、自分はこの記述から「どちらかを使うかは利用者の好みです」と解釈をしておりました。

ドキュメントとしてスタイルを明確に主張するべきか悩んだ結果、インタフェースを優先して利用するべきかはプロジェクトごとに決めるべきスタイルガイドであり、そのスタイルガイドを今回のドキュメントでそうあるべきと記述するのは少し混乱を招くかな?と思っておりました。

例えば、ReactではPropsの型を定義するときに慣例として型エイリアスが利用されますが、Googleのスタイルガイド等に従って、「Interfaceを優先的に使いましょう」と書いた場合に初学者はなぜReactのPropsの型定義はInterfaceを使わないんだ?という疑問が生まれて余計な混乱が生じてしまうかなということを懸念していました。
(後から下手に拡張させたくないので、Propsでは型エイリアスを使うなどの理由があるかもしれないのですが、自分の知識不足でそれに対する明確な回答は持っていませんでした。)

以上のことと公式ドキュメントの記述を踏まえて、当時は「正解はありません」と書いていました。🙆🏻‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stage1:話し合い中 結論を出すために話し合いをしている最中です。まだ着手・プルリクエストの作成はしないでください。
Projects
None yet
Development

No branches or pull requests

3 participants