TypeScriptの高度なジェネリック。Yandexレポート

ジェネリックス、またはパラメーター化された型を使用すると、より柔軟な関数とインターフェースを記述できます。単一の型によるパラメーター化よりも先に進むには、ジェネリックスのいくつかの一般原則を理解するだけでよく、秘密の箱が好きになる前にTypeScriptが開きます。 アレクサンドルニコライチェフ ジェネリックを互いにネストし、プロジェクトで自動型推論を使用することを恐れない方法を説明しました。



-みなさん、こんにちは。私の名前はアレクサンダーニコライチェフです。私はYandex.Cloudでフロントエンド開発者として働いており、Yandexの内部インフラストラクチャに取り組んでいます。今日は非常に便利なことについてお話しますが、それなしでは、特に大規模な最新のアプリケーションを想像することは困難です。 これはTypeScript、タイピング、より狭いトピック-ジェネリックス、そしてそれらが必要な理由です。



まず、TypeScriptがなぜであり、インフラストラクチャがTypeScriptと何の関係があるのか​​という質問に答えましょう。インフラストラクチャの主な特性は、その信頼性です。これをどのように保証できますか?まず、テストできます。





ユニットテストと統合テストがあります。テストは良い標準的な方法です。



また、コードレビューを使用する必要があります。さらに-エラーのコレクション。それでもエラーが発生した場合は、特別なメカニズムがエラーを送信し、すぐに修正できます。



間違えないのはなんて素晴らしいことでしょう。このために、タイピングがあります。これにより、実行時にエラーが発生することはまったくありません。Yandexは業界標準のTypeScriptを使用しています。そして、アプリケーションは大きくて複雑なので、次の式が得られます。フロントエンド、タイピング、さらには複雑な抽象化さえあれば、間違いなくTypeScriptジェネリックになります。それらなしではできません。



構文



基本的な教育プログラムを実施するために、最初に構文の基本を見てみましょう。



TypeScriptのジェネリックは、別のタイプに依存するタイプです。



単純なタイプのPageがあります。特定のパラメーター<T>を使用してパラメーター化します。これは、山かっこで囲まれています。そして、いくつかの文字列、数字があることがわかりますが、<T>は可変です。



インターフェイスと型に加えて、同じ構文を関数に適用できます。つまり、同じ<T>パラメーターが関数の引数に転送され、応答で同じインターフェイスを再利用し、そこにも渡します。



一般的な呼び出しも、初期化されたときと同じように、目的のタイプの山かっこで記述されます。



クラスにも同様の構文があります。パラメータをプライベートフィールドにスローし、一種のゲッターがあります。しかし、そこには型を書きません。どうして? TypeScriptはタイプを推測できるからです。これは彼の非常に便利な機能であり、これを適用します。



このクラスを使用するとどうなるか見てみましょう。インスタンスを作成し、<T>パラメーターの代わりに、列挙要素の1つを渡します。ロシア語、英語の列挙型を作成します。 TypeScriptは、列挙型から要素を渡したことを理解し、lang型を推測します。



しかし、型推論がどのように機能するかを見てみましょう。列挙要素の代わりにこの列挙から定数を渡す場合、TypeScriptは、これが列挙全体ではなく、すべての要素ではないことを理解します。そして、そのタイプの特定の値、つまりlang en、Englishがすでに存在します。



文字列など、他の何かを渡すと、列挙と同じ意味を持つように見えます。しかし、これはすでに文字列であり、TypeScriptの別のタイプであり、取得します。また、文字列を定数として渡すと、文字列の代わりに定数、文字列リテラルがあります。これらはすべて文字列ではありません。この場合、特定の文字列enがあります。



では、これをどのように拡張できるか見てみましょう。



1つのパラメータがありました。複数のパラメーターを使用することを妨げるものは何もありません。それらはすべてコンマで区切られて書かれています。同じ山かっこで、最初から3番目まで順番に適用します。呼び出されたときに必要な値に置き換えます。



数値リテラルの連結、いくつかの標準タイプ、文字列リテラルの連結を考えてみましょう。それらはすべて単に順番に書き留められています。



これが関数でどのように発生するかを見てみましょう。ランダム関数を作成します。最初の引数または2番目の引数をランダムに与えます。



最初の引数はタイプA、2番目の引数はタイプBです。したがって、それらの和集合が返されます:thisまたはthisのいずれか。まず、関数を明示的に入力できます。 Aは文字列、Bは数値であることを示します。 TypeScriptは、明示的に指定したものを調べて、タイプを推測します。



ただし、型推論を使用することもできます。重要なことは、推論されるのは型だけではなく、引数の可能な最小の型であることを知ることです。



引数である文字列リテラルを渡し、それがタイプAに対応し、2番目の引数である1がタイプBに対応する必要があるとします。文字列リテラルと1で可能な最小値は、リテラルAと同じものです。TypeScriptはこれを私たちに出力します。そのようなタイプの狭まりが判明しました。



次の例に進む前に、タイプが一般的に相互にどのように関係しているか、これらの関係を使用する方法、すべてのタイプの混乱から秩序を取り除く方法を見ていきます。



タイプの関係



タイプは、従来、一種のセットと見なすことができます。タイプのセット全体の一部を示す図を見てみましょう。



その中のタイプは、ある種の関係によって接続されていることがわかります。しかし、どれですか?これらは半順序関係です。つまり、型は常にそのスーパータイプ、つまり、可能なすべての値をカバーするその「上」の型で指定されます。



反対方向に進むと、各タイプは「少ない」サブタイプを持つことができます。



文字列のスーパータイプは何ですか?文字列を含む結合。数字のある文字列、数字の配列のある文字列など、何でも。サブタイプはすべて文字列リテラルです:a、b、c、またはac、またはab。



ただし、順序は線形ではないことを理解することが重要です。つまり、すべてのタイプを比較できるわけではありません。これは論理的であり、これが型の不一致エラーにつながる原因です。つまり、文字列を単純に数値と比較することはできません。



そして、この順序で、いわば最上位のタイプがあります-不明です。そして、最も低い、空のセットのアナログは決してありません。どのタイプのサブタイプでもありません。そして、不明はあらゆるタイプのスーパータイプです。



そしてもちろん、例外があります-いずれか。これは特別なタイプであり、この順序を完全に無視し、タイプを気にしないためにJavaScriptから移行する場合に使用されます。ゼロから使用することはお勧めしません。タイプの順序をあまり気にしない場合は、これを行う価値があります。



この注文の知識が私たちにどのような知識を与えるか見てみましょう。



パラメータをスーパータイプに制限できます。キーワードはextendsです。パラメータを1つだけ持つジェネリック型を定義します。ただし、文字列のサブタイプまたは文字列自体のみである可能性があると言います。番号を転送することはできません。これにより、タイプエラーが発生します。関数を明示的に入力すると、パラメーターでは、文字列のサブタイプまたは文字列(appleとorange)のみを指定できます。両方の文字列は、文字列リテラルを連結したものです。検証に合格しました。





引数に基づいて、自分で型を自動的に推測することもできます。文字列リテラルを渡した場合、これも文字列です。チェックは機能しました。



これらの制限を拡張する方法を見てみましょう。



私たちは自分たちを一行に限定しました。しかし、文字列は単純すぎる型です。オブジェクトキーを操作したいと思います。それらを操作するには、まず、オブジェクトキー自体とそのタイプがどのように配置されているかを理解します。



特定のオブジェクトがあります。文字列、数値、ブール値、名前によるキーなど、いくつかの種類のフィールドがあります。キーを取得するには、keyofキーワードを使用します。すべてのキー名の和集合を取得します。



値を取得したい場合は、角括弧構文を使用して取得できます。これはJS構文に似ています。タイプのみを返します。キーのサブセット全体を渡すと、一般にこのオブジェクトのすべての値の和集合が得られます。



パーツを取得したい場合は、それを指定できます-すべてのキーではなく、一部のサブセット。指定されたキーに対応するフィールドのみを受け取ることを期待しています。すべてを1つのケースに減らすと、これは1つのフィールドになり、1つのキーで1つの値が得られます。このようにして、対応するフィールドを取得できます。



オブジェクトキーの使い方を見てみましょう。



extendsキーワードの後に​​は、任意の有効な型が存在する可能性があることを理解することが重要です。他のジェネリックから形成されたものやキーワードの使用を含みます。



これがkeyofでどのように機能するかを見てみましょう。 CustomPickタイプを定義しました。実際、これはTypeScriptからのPickライブラリタイプのほぼ完全なコピーです。彼は何をしているの?



2つのパラメータがあります。 2番目は単なるパラメータではありません。それは最初の鍵でなければなりません。 <T>からkeyofを拡張していることがわかります。したがって、それはキーのサブセットである必要があります。



次に、このサブセットの各キーKについて、オブジェクトを実行し、同じ値を入力して、構文の疑問符を除いたオプションを特別に削除します。つまり、すべてのフィールドが必須になります。



アプリケーションを見てみましょう。フィールドの名前というオブジェクトがあります。それらのサブセット(a、b、c、または一度にすべて)のみを取得できます。私たちはaまたはcを取りました。対応する値のみが表示されますが、比較的言えば疑問符を削除したため、フィールドが必須になっていることがわかります。このタイプを定義して使用しました。誰も私たちにこのジェネリックを取り上げて別のジェネリックに押し込むことを気にしません。



これはどのように起こりますか?別のタイプ、カスタムを定義しました。 2番目のパラメーターは、keyofではなく、右に示したジェネリックを適用した結果を展開します。それはどのように機能しますか、私たちはそれに何を転送していますか?



オブジェクトとそのすべてのキーをこのジェネリックに渡します。これは、出力がすべての必須フィールドを持つオブジェクトのコピーになることを意味します。ジェネリックを別のジェネリックにネストするというこのチェーンは、タスクに応じて無期限に継続し、コードを構造化することができます。ジェネリックスなどに再利用可能な構造を導入します。



指定された引数は、順序どおりである必要はありません。 Pパラメータのようなものは、CustomPickジェネリックのTキーを展開します。しかし、それを最初のパラメーターとして、Tを2番目のパラメーターとして示すことを誰も気にしませんでした。 TypeScriptは、パラメーター間を順番に移動しません。彼は私たちが指定したすべてのパラメーターを調べます。次に、彼は特定の連立方程式を解き、このシステムを満たす型の解を見つけた場合、型チェックに合格しました。



この点で、パラメータが互いのキーを展開するような面白いジェネリックを導出できます。a-これらはキーb、b-キーaです。どうしてこれが鍵の鍵になるのだろうか?しかし、TypeScript文字列は実際にはJavaScript文字列であり、JavaScript文字列には独自のメソッドがあることがわかっています。したがって、任意の文字列メソッド名で問題ありません。文字列メソッドの名前も文字列であるためです。そしてそこから彼女は彼女の名前を持っています。



したがって、このような制限を得ることができ、必要なタイプを指定すれば連立方程式が解かれます。



これを実際にどのように使用できるか見てみましょう。 APIに使用します。 Yandexアプリケーションが展開されているサイトがあります。プロジェクトとそれに対応するサービスを表示したいと思います。



この例では、開発者向けにqyp仮想マシンを実行するプロジェクトを取り上げました。このオブジェクトの構造がバックエンドにあることがわかっているので、ベースから取得します。しかし、プロジェクトの他に、ドラフト、リソースなどの他のオブジェクトがあります。そして、それらはすべて独自の構造を持っています。



さらに、オブジェクト全体ではなく、サービスの名前と名前の2つのフィールドを要求します。そのような機会があります。バックエンドを使用すると、パスを渡して不完全な構造を受け取ることができます。 DeepPartialについてはここで説明します。少し後でそれを設計する方法を学びます。ただし、これは、オブジェクト全体ではなく、オブジェクトの一部が転送されることを意味します。



これらのオブジェクトを要求する関数を記述したいと思います。 JSで書いてみましょう。しかし、よく見るとタイプミスがあります。 「Projeact」のタイプでは、パスにサービスのタイプミスもあります。良くありません。エラーは実行時に発生します。



TSバリアントは、パスを除けばそれほど違いはないようです。ただし、実際には、バックエンドにある値以外に、[タイプ]フィールドに他の値を含めることはできないことを示します。



パスフィールドには、他の欠落しているフィールドを選択できないようにする特別な構文があります。必要なネストのレベルをリストしてオブジェクトを取得する関数を使用します。実際、この関数からパスを取得することは、実装の懸念事項です。ここに秘密はありません、彼女はプロキシを使用します。これは私たちにとってそれほど重要ではありません。



関数を取得する方法を見てみましょう。





私たちには機能、その使用法があります。この構造があります。まず、すべての名前を取得します。名前が構造と一致するタイプを記述します。



プロジェクトについて、そのタイプをどこかに記述しているとしましょう。私たちのプロジェクトでは、一般的なリポジトリで利用可能なprotobufファイルからtaipingを生成します。次に、プロジェクト、ドラフト、リソースのすべてのタイプが使用されていることがわかります。



実装を見てみましょう。順番に見ていきましょう。



機能があります。まず、それがどのようにパラメータ化されているかを見てみましょう。これらの前述の名前だけで。それが何を返すか見てみましょう。値を返します。なぜそうなのですか?角括弧構文を使用しました。ただし、型に1つの文字列を渡すため、使用する場合の文字列リテラルの連結は常に1つの文字列になります。プロジェクトとリソースの両方である文字列を同時に作成することはできません。彼女はいつも一人で、意味も同じです。



すべてをDeepPartialでラップしましょう。オプションタイプ、オプション構造。最も興味深いのはパラメータです。私たちは別のジェネリックの助けを借りて彼らに尋ねます。



汎用パラメーターがパラメーター化されるタイプも、関数の制約と一致します。名前のタイプ(プロジェクト、リソース、ドラフト)のみを受け入れることができます。IDはもちろん文字列であり、私たちはそれに興味がありません。これが私たちが示したタイプで、3つのうちの1つです。パス機能はどのように機能するのだろうか。これは別のジェネリックです-なぜそれを再利用しませんか。実際、オブジェクトは任意のタイプのフィールドを持つことができるため、どのタイプのフィールドかわからないため、任意の配列を返す関数を作成するだけです。この実装では、タイプを制御できます。



誰かがそれを単純だと思ったら、制御構造に移りましょう。



コントロールコンストラクト



2つの構造のみを検討しますが、必要なほとんどすべてのタスクをカバーするには十分です。



条件付きタイプとは何ですか?これらはJavaScriptのターナークと非常によく似ていますが、タイプのみが異なります。タイプaがbのサブタイプであるという条件があります。その場合は、cを返します。そうでない場合は、dを返します。つまり、タイプの場合のみ、これは正常です。



それがどのように機能するか見てみましょう。 CustomExcludeタイプを定義します。これは、基本的にライブラリExcludeをコピーします。必要な要素を型共用体から破棄するだけです。 aがbのサブタイプの場合は空を返し、そうでない場合はaを返します。結合で機能する理由を見ると、これは奇妙です。



特別な法則が役立ちます。つまり、ユニオンがあり、extendsを使用して条件をチェックする場合、各要素を個別にチェックしてから、それらを再度結合します。これは、条件付きタイプの場合のみ、このような推移的な法則です。



CustomExcludeを使用する場合、各観測項目を順番に確認します。 aはaを展開し、aはサブタイプですが、voidを返します。 bはaのサブタイプですか?いいえ-戻りますb。 cはどちらのサブタイプでもありません。cを返します。次に、残っているもの、すべてのプラス記号を組み合わせて、bとcを取得します。私たちはを捨てて、欲しいものを手に入れました。



同じ手法を使用して、タプルのすべてのキーを取得できます。タプルが同じ配列であることはわかっています。つまり、JSメソッドがありますが、これは必要ありません。必要なのはインデックスだけです。したがって、タプルのすべてのキーからすべてのメソッドの名前を破棄し、インデックスのみを取得するだけです。



前述のDeepPartialタイプをどのように定義しますか?これは、再帰が初めて使用される場所です。オブジェクトのすべてのキーを調べて調べます。値はオブジェクトですか?その場合は、再帰的に適用します。そうでない場合で、これが文字列または数値の場合は、そのままにして、すべてのフィールドをオプションにします。それはまだ部分型です。



この再帰呼び出しと条件付き型により、TypeScriptチューリングは実際に完了します。しかし、これを喜ぶために急いではいけません。あなたがこのようなことをしようとすると、それはあなたを怒らせます、多くの再帰性を持つ抽象化。



TypeScriptはこれを監視し、コンパイラのレベルでもエラーをスローします。そこで何かが数えられるまで待つことすらありません。また、呼び出しが1つしかないこのような単純なケースでは、再帰が非常に適しています。



それがどのように機能するか見てみましょう。オブジェクトのフィールドにパッチを適用する問題を解決したいと思います。アプリケーションの展開を計画するために仮想クラウドを使用しており、リソースが必要です。



CPUリソース、コアを使用したとしましょう。誰もがカーネルを必要としています。例を単純化したところ、リソースだけがあり、カーネルだけがあり、それらは数値です。



それらにパッチを適用し、値にパッチを適用する関数を作成したいと思います。カーネルを追加または減算します。同じJavaScriptで、ご想像のとおり、タイプミスがあります。ここでは、文字列に数値を追加します-あまり良くありません。



TypeScriptではほとんど何も変更されていませんが、実際、IDEレベルのこのコントロールは、この文字列または特定の数値以外は渡せないことを示しています。



これを実現する方法を見てみましょう。このような関数を取得する必要があり、この種のオブジェクトがあることはわかっています。番号とフィールドのみにパッチを適用していることを理解する必要があります。つまり、数字があるフィールドのみの名前を取得する必要があります。フィールドは1つだけで、それは数値です。



これがTypeScriptでどのように実装されているか見てみましょう。



関数を定義しました。パッチを適用するオブジェクトとフィールド名の3つの引数があります。しかし、これは単なるフィールド名ではありません。数値フィールドの名前のみにすることができます。これがどのように行われるかを見ていきます。そして、純粋関数であるパッチャー自体。



ある種の非人称的な機能、パッチがあります。私たちはその実装には興味がありませんが、数値だけでなく条件ごとに任意のフィールドのキーを取得するために、このような興味深い型を取得する方法に興味があります。ここに番号があります。



これがどのように発生するかを順番に分析してみましょう。



渡されたオブジェクトのすべてのキーを調べてから、この手順を実行します。オブジェクトフィールドが目的のサブタイプ、つまり数値フィールドであることを確認しましょう。はいの場合、フィールド値ではなくフィールド名を書き込むことが重要です。それ以外の場合は、通常、何も記述しません。



しかし、その後、そのような奇妙なオブジェクトが判明しました。すべての数値フィールドの名前が値として使用されるようになり、数値以外のフィールドはすべて空になりました。次に、この奇妙なオブジェクトのすべての値を取得します。



ただし、すべての値には空が含まれており、組み合わせると空が崩壊するため、数値のフィールドに対応するフィールドのみが残ります。つまり、必要なフィールドのみを取得しました。



例は次のとおりです。単純なオブジェクトがあり、フィールドは1つです。これは数字ですか?はい。フィールドは数字です、それは数字ですか?はい。最後の行は数字ではありません。必要な数値フィールドのみを取得します。



これで整理しました。私は最後に最も難しいものを残しました。これは型推論です-推論。条件付き構文で型をキャプチャします。





条件付き構成でのみ機能するため、前のトピックと切り離せません。



それはどのように見えますか?配列の要素を知りたいとしましょう。ある種の配列が来たので、特定の要素を知りたいのですが。私たちは見ます:私たちはある種の配列を受け取りました。これは、変数xの配列のサブタイプです。はいの場合-このx、配列要素を返します。そうでない場合は、空を返します。



この状態では、任意の配列で型をパラメーター化したため、2番目のブランチは実行されません。もちろん、それは何かの配列になります。なぜなら、anyの配列は要素を持つしかないからです。



文字列の配列を渡すと、文字列が返されることが期待されます。また、ここで定義されているのはタイプだけではないことを理解することが重要です。文字列の配列から視覚的に明らかです。文字列があります。しかし、タプルを使用すると、すべてがそれほど単純ではありません。可能な限り最小のスーパータイプが決定されていることを知ることは重要です。すべての配列が、いわば、anyまたはunknownを持つ配列のサブタイプであることは明らかです。この知識は私たちに何も与えません。可能な限り最小限のことを知ることは私たちにとって重要です。



タプルを渡すとします。実際、タプルも配列ですが、この配列にどの要素があるかをどのように判断すればよいでしょうか。数値の文字列タプルがある場合、それは実際には配列です。ただし、要素は同じタイプである必要があります。また、文字列と数値の両方がある場合は、和集合になります。



TypeScriptはこれを出力し、そのような例では文字列と数値の連結を正確に取得します。



1つの場所でキャプチャを使用できるだけでなく、必要な数の変数を使用することもできます。タプルの要素を単純に交換する型を定義するとします。最初の要素と2番目の要素です。最初の要素、2番目の要素を取得し、それらを交換します。



しかし実際には、それでいちゃつくことはお勧めできません。通常、タスクの90%では、1種類のキャプチャだけで十分です。





例を見てみましょう。目的:リクエストの状態に応じて、良いオプションか悪いオプションのどちらかを示す必要があります。これは、アプリケーション展開サービスのスクリーンショットです。エンティティ、ReplicaSet。バックエンドからのリクエストがエラーを返した場合は、それをレンダリングする必要があります。同時に、バックエンド用のAPIがあります。 Inferがそれと何の関係があるのか​​見てみましょう。



私たちは、最初にreduxを使用し、次にreduxthunkを使用していることを知っています。そして、これを実行できるように、ライブラリサンクを変換する必要があります。私たちには悪い方法と良い方法があります。



そして、reduxツールキットのextraReducersに入る良い方法は次のようになっていることを私たちは知っています。 PayLoadがあることはわかっているので、バックエンドから届いたカスタムタイプを取得したいだけでなく、リクエストの良し悪しに関する情報も取得します。エラーがあるかどうか。この出力にはジェネリックが必要です。



JavaScriptについては意味がないので、比較はしていません。 JavaScriptでは、原則として、型を制御することはできず、メモリのみに依存します。単に1つがないので、ここに悪いオプションはありません。



このタイプが必要なことはわかっています。しかし、私たちには行動があるだけではありません。このアクションでディスパッチを呼び出す必要があります。そして、リクエストキーでエラーを表示する必要があるこのビューが必要です。つまり、withRequestKeyメソッドを使用して、reduxサンクの上にこのような追加機能を組み合わせる必要があります。



もちろん、このメソッドがありますが、元のAPIメソッドであるgetReplicaSetもあります。それはどこかに書かれていて、ある種のアダプターを使用してreduxサンクをオーバーライドする必要があります。それを行う方法を見てみましょう。



このような関数を取得する必要があります。それは非常に多くの追加機能を備えたサンクです。怖いように聞こえますが、心配しないでください。今度は棚で分解して、はっきりと見えるようにします。



元のライブラリタイプを拡張するアダプタがあります。追加のwithRequestKeyメソッドとこのライブラリタイプへのカスタム呼び出しを組み合わせるだけです。ジェネリックの主な機能が何であるか、どのパラメーターが使用されているかを見てみましょう。



1つ目は、メソッドを持つオブジェクトであるAPIです。 getReplicaSetを実行したり、プロジェクトやリソースを取得したりできます。問題ではありません。現在のメソッドでは特定のメソッドを使用しており、2番目のパラメーターはメソッドの名前です。次に、要求している関数のパラメーターを使用します。Parametersライブラリタイプを使用します。これはTypeScriptタイプです。同様に、バックエンド応答にはReturnTypeライブラリタイプを使用します。これは、関数が返したものです。



次に、カスタム出力を、ライブラリから提供されたAsyncThunkタイプに渡すだけです。しかし、この結論は何ですか?これは別のジェネリック医薬品です。実際、それは単純に見えます。サーバーからの応答だけでなく、渡したパラメーターも保存します。レデューサーでそれらを追跡するためだけに。次に、withRequestKeyを見てみましょう。私たちの方法は、キーを追加するだけです。彼は何を返しますか?再利用できるので同じアダプター。 withRequestKeyを作成する必要はまったくありません。これは単なる追加機能です。ラップして同じアダプターを再帰的に返し、同じものをそこに渡します。



最後に、このサンクが私たちに返したものをレデューサーに出力する方法を見てみましょう。





このアダプターがあります。主なことは、API、APIメソッド、パラメーター(入力)、出力の4つのパラメーターがあることを覚えておくことです。抜け出す必要があります。ただし、サーバー応答と要求パラメーターの両方のカスタム出力があることを覚えています。



Inferでこれを行うにはどうすればよいですか?このアダプタは入力に供給されていることがわかりますが、通常は任意です:any、any、any、any。このタイプを返す必要があります。これは、サーバーの応答と要求パラメーターのようになります。そして、入り口がどこにあるべきかを見ています。 3番目に。ここにタイプキャプチャを配置します。入り口があります。同様に、出口は4位です。



TypeScriptは構造化されたタイピングに基づいています。彼はこの構造を分解し、入口が3番目にここにあり、出口が4番目にあることを理解します。そして、必要なタイプを返します。



したがって、型推論を実現し、Reducer自体ですでにそれらにアクセスできます。JavaScriptでこれを行うことは基本的に不可能です。



All Articles