TypeScriptのinferとは何か
Tweet本日の勉強会で、TypeScriptのこのPRをみてinferとは何かを調べたので備忘録として。
結論
- 型定義ファイルを作るときに利用する。
- 依存している型を三項演算子の値として利用することができようにするためのもの。(@todo: この説明の正確性は要検討。)
例
JavaScriptで次の様なコードが実装されているとします。
簡単な、クイズを行うクラスです。
1 | class Questioner |
クライアントコードはこんな感じです。
1 | const questioner = QuestionerFactory.create({ |
ちょっと質問内容が難しすぎます。
Questionerを利用する側では、せめてassertで引数の型ぐらい教えて欲しいものです。
TypeScriptによる型
TypeScriptでは型がありますので、型定義ファイルを作成することで
assertメソッドの引数の型を絞り込むことができます。
1 | declare namespace QuestionerFactory { |
この様にassertメソッドの引数の型がstringであるということがわかる様になります。
解説
ジェネリクス
1 | function create<T>(question: T): Questioner<T>; |
ここは、ジェネリクスです。
QuestionerFactory.createメソッドの引数T
をQuestionerのX
として定義しています。
つまり、次の様なコードを書いたときの返り値はQuestioner<string>
です。
1 | QuestionerFactory.create<string>('sample'); // returns Questioner<string> |
型エイリアス
1 | type Expected<Y> = Y extends { expected: infer Z } ? Z : never; |
type
は型エイリアスです。
1 | type hoge = string|number; |
このように書くことで、fuga関数のvalueにはstringかnumberを入れる様に型定義することができます。
ConditionalTypes
1 | type Expected<Y> = Y extends { expected: infer Z } ? Z : never; |
型を条件分岐して定義することができます。
例えば、次の例では、Y
にstring
が拡張された型が定義された場合はnubmer
になり、それ以外の場合はnever
になります。
1 | type Sample<Y> = Y extends string ? number : never; |
三項演算子で分岐を定義しています。
次の様な、三項演算子のネストして記述することも可能な様です。
1 | type Sample<Y> = Y extends string ? number : |
このコードは次のフローになります。
Type inference in ConditionalTypes
1 | type Expected<Y> = Y extends { expected: infer Z } ? Z : never; |
inferでは定義したY
が依存している型をZ
として利用することができます。
次の様なフローになります。
この様に、依存している型を三項演算子の値として利用することができます。
Type inference in ConditionalTypes
まとめ
1 | declare namespace QuestionerFactory { |
この型定義は次の様なフローになります。
このため、assertメソッドの引数の型がZであるということがわかる様になります。
この記事のSinonのStubについて説明している例が、実例として非常にわかりやすかったです。
https://qiita.com/Quramy/items/b45711789605ef9f96de#%E4%BE%8B2-sinonjs%E3%81%AEstub