序文: 面接のとき、私はたいてい応募者に奇妙な質問をするのが好きです。たとえば、ライブラリの作成者の場合、特定の機能をどのように実装しますか?一般的に、このタイプの質問には正解はありません。主な目的は、候補者がこのライブラリについてより深く理解しているかどうかをテストすることです。2 番目の目的は、それが楽しいということです。楽しむのは楽しいですが、真剣になるときには真剣にならなければなりません。以前、 この事件が受験生に与えた影響は大きいか小さいかは別として、私にとっては大きな衝撃でした。それがジェネリック医薬品についての記事を書くきっかけとなりました。しかし、この種を植えて以来、私は後悔し始めました。 TS のジェネリックについて詳しく知れば知るほど、このトピックについて書くことはあまりないと感じます。まず第一に、TS におけるジェネリックは空気のようなもので、よく使われますが、説明するのは難しいものです。第二に、範囲が広すぎてすべてを網羅するのは困難です。 今日の投稿は、このシリーズのこれまでの投稿とは異なります。この記事では、C++ テンプレートが解決する必要がある問題から始めて、TS ジェネリックが解決する必要がある問題を紹介し、少し高度な使用シナリオを簡単に紹介します。 1. テンプレートジェネリックについて言えば、ジェネリックの創始者であるテンプレートについて言及する必要があります。 C++ のテンプレートは面倒でありながら強力であることで知られており、長年にわたってさまざまな主要な教科書で取り上げられてきました。現時点では、Java、.NET、または TS のジェネリックは、C++ テンプレートのサブセットを実装するものと考えられます。私はサブセットの記述に同意しません。目的の点では、TS テンプレートと C++ テンプレートは完全に異なるためです。 C++ テンプレートは、型安全な汎用コンテナを作成するために作成されました。まず、一般的なコンテナについてお話ししましょう。たとえば、リンク リストや配列を記述する場合、このデータ構造は、その中の特定のデータのタイプをあまり気にせず、対応する操作を実装できます。しかし、js 自体は型やサイズを気にしないので、js 内の配列はもともとユニバーサル コンテナーです。 TS の場合、ジェネリック医薬品の出現によりこの問題を解決できます。比較する価値のあるもう 1 つの点は生成です。C++ テンプレートは最終的に対応するクラスまたは関数を生成しますが、TS の場合、TS は何も生成できません。学生の中には、TS は最終的に JS コードを生成しないのかと疑問に思う人もいるかもしれません。これは少し不正確です。TS は最終的に元のロジックに何もせずに JS コードを分離するからです。 C++ テンプレートのもう 1 つの目的はメタプログラミングです。このメタプログラミングは非常に強力であり、主にコンパイル時のプログラミング構造を通じてプログラムの実行を最適化します。 TS に関する限り、現在のところ、同様の最適化は 1 つだけ行われ、つまり、const enum を実行場所でインライン化できるだけです。このタイプの最適化に関しては、前回の記事の最後でも型推論に基づく最適化について触れましたが、現時点では TS にはこの機能はありません。これらの単純な最適化がサポートされていない場合、より複雑なメタプログラミングはさらに不可能になります (メタプログラミングでは、ジェネリック パラメーターの論理的な推論と、最終的にそれらが使用される場所にインライン化する必要があります)。 C++ テンプレートについて私が言いたいことはこれですべてです。結局のところ、これはテンプレート メタプログラミングに関する記事ではなく、私は専門家ではありません。テンプレートについてさらに質問がある場合は、Lunzi 兄弟に尋ねてください。たくさんのテンプレートについて話した後、私が主に言いたいのは、TS のジェネリックとテンプレートは非常に異なるということです。 2. ジェネリックTS におけるジェネリックの主な用途は 3 つあると思います。
2 点目と 3 点目については、前回の記事で明確に述べましたので、ここでは繰り返しません。最初の点に関して、2つの例を挙げてみましょう。 最初の例は、汎用コンテナに関するものです。単純な汎用リンク リストを実装するとします。コードは次のとおりです。 class LinkedList<T> { // ジェネリッククラス value: T; next?: LinkedList<T>; // 型宣言には自身を使用できます。constructor(value: T, next?: LinkedList<T>) { this.value = 値; this.next = 次へ; } ログ(){ if (this.next) { this.next.log(); } console.log(この値); } } let list: LinkedList<number>; // ジェネリック特殊化は number です [1, 2, 3].forEach(値 => { リスト = 新しい LinkedList(値、リスト); }); リスト.log(); // 1 2 3 2 つ目は汎用コンポーネントです。汎用フォーム コンポーネントを実装する場合は、次のように記述できます。 関数 Form<T は { [key: string]: any }>({ data }: { data: T }) を拡張します { 戻る ( <フォーム> {data.map((値, キー) => <入力名={キー} 値={値} />)} </フォーム> ) } この例では、汎用コンポーネントを示すだけでなく、 extends を使用して汎用制約を定義する方法も示します。実際の汎用フォーム コンポーネントはこれよりも複雑になる可能性がありますが、上記は単にアイデアを示すためのものです。 ここまでで、TSジェネリックについての説明は終わりです。しかし、この記事はまだ終わりではありません。ジェネリックの高度な使用テクニックをいくつか見てみましょう。 3. ジェネリック再帰簡単に言えば、再帰とは、関数の出力を論理計算の入力として継続的に使用できる問題を解決する方法です。簡単な例を見てみましょう。たとえば、加算を計算したい場合、2 つの数値の合計のみを計算できる 再帰は現実の生活では非常に一般的なので、その存在を見落としてしまうことがよくあります。プログラミングの世界でも同じことが言えます。 TS で再帰がどのように実装されているかを示す例を次に示します。たとえば、関数の戻り値の型を返すことができるジェネリック型 ReturnType<T> ができました。しかし、呼び出し階層が非常に深い関数があり、その深さがわかりません。どうすればよいでしょうか? アイデア1: 型 DeepReturnType<T extends (...args: any) => any> = ReturnType<T> extends ( ...引数: 任意 ) => 任意 ? DeepReturnType<ReturnType<T>> // ここで自身を参照します: ReturnType<T>; 上記コードの説明: ここではジェネリック型 直感的でシンプルな解決策の背後には、必ず「しかし」があります。ただし、これをコンパイルすることはできません。主な理由は、TS が現在サポートされていないことです。将来的にサポートされるかどうかはわかりませんが、公式の理由は非常に明確です。
では、このような需要にどう応えればよいのでしょうか?公式のアイデアのように、再帰の回数を制限して使用する方法があります。私のアイデアは次のとおりです: // 2層のジェネリック型 type ReturnType1<T extends (...args: any) => any> = ReturnType<T> extends ( ...引数: 任意 ) => 任意 ? 戻り値の型<戻り値の型<T>> : 戻り値の型<T>; // 3層ジェネリック型 type ReturnType2<T extends (...args: any) => any> = ReturnType<T> extends ( ...引数: 任意 ) => 任意 ? 戻り値1<戻り値<T>> : 戻り値の型<T>; // ほとんどのケースに対応できる 4 層のジェネリック型 type DeepReturnType<T extends (...args: any) => any> = ReturnType<T> extends ( ...引数: 任意 ) => 任意 ? 戻り値2<戻り値<T>> : 戻り値の型<T>; // テスト const deep3Fn = () => () => () => () => "flag is win" as const; // 4 層関数 type Returned = DeepReturnType<typeof deep3Fn>; // type Returned = "flag is win" const deep1Fn = () => "flag is win" as const; // 1 層関数 type Returned = DeepReturnType<typeof deep1Fn>; // type Returned = "flag is win" この手法を拡張して、 4. デフォルトのジェネリックパラメータジェネリックが大好きな場合もありますが、クラスや関数の消費者が毎回ジェネリック型を指定することを望まない場合もあります。このような場合は、デフォルトのジェネリック パラメータを使用できます。これは、次のような多くのサードパーティ ライブラリで広く使用されています。 // PSCを受け取る汎用コンポーネント class Component<P,S,C> { 小道具: P; 状態: S; コンテキスト:C .... } // MyComponent は Component<{}, {}, {}>{} を拡張するクラスを使用する必要があります // しかし、コンポーネントが props、state、context を必要としない純粋なコンポーネントの場合はどうなるでしょうか? // 次のように class Component<P = {}, S = {}, C = {}> を定義できます。 小道具: P; 状態: S; コンテキスト:C .... } // 次に、クラス MyComponent を Component {} に拡張して使用できます。 この機能は非常に実用的だと思います。C++ テンプレートの 5. ジェネリックオーバーロードジェネリックオーバーロードについては、公式ドキュメントで何度も言及されています。この種のオーバーロードは、関数オーバーロードのいくつかのメカニズムに依存します。したがって、まずは TS での関数オーバーロードについて見てみましょう。ここでは、 定数平方 = (n) => n * n; // 受信関数のマップ マップ({ 'a': 4, 'b': 8 }, 正方形); // => [16, 64] (反復順序は保証されません) const ユーザー = [ { 'ユーザー': 'バーニー' }, { 'ユーザー': 'フレッド' } ]; // 文字列のマップを受け取る ユーザーをマップします。'user'; // => ['バーニー', 'フレッド'] では、TS でこのような型宣言をどのように表現するのでしょうか?次のように関数オーバーロードを使用できます。 // これはデモンストレーションのみであり、正確性は保証されません。実際のシナリオでは、ここに正しいタイプを入力する必要があります。 インターフェース MapFn { (obj: any, prop: string): any; // 文字列を受け取る場合、シナリオ 1 (obj: any, fn: (value: any) => any): any; // 関数を受け取る場合、シナリオ 2 } 定数マップ: MapFn = () => ({}); map(users, 'user'); // オーバーロードシナリオ 1 map({ 'a': 4, 'b': 8 }, square); // オーバーロードシナリオ 2 上記のコードは、TS のかなり特殊なメカニズムを使用しています。つまり、関数の定義、新しいクラス関数、およびその他のクラス関数を もちろん、次のような、より伝統的なアプローチを使用することもできます。 関数 map(obj: any, prop: string): any; 関数 map(obj: any, fn: (value: any) => any): any; 関数 map(obj, secondary): 任意 {} ここでは関数オーバーロードについて基本的に説明します。ジェネリックに一般化すると、基本的には同じです。これは友人が提起した質問の例です。ここではこの質問について詳しく説明しません。解決策はおそらくこれです: インターフェースFN { (obj: { 値: 文字列; onChange: () => {} }): void; <T は {[P in keyof T]: never}> を拡張します (obj: T): void; // T 型の obj の場合、他のキーを受け取ることはありません。 } 定数fn: FN = () => {}; fn({}); // OK fn({ value: "Hi" }); // 間違い fn({ onChange: () => {} }); // 間違い fn({ value: "Hi", onChange: () => ({}) }); // OK React エコシステムでは、ジェネリック オーバーロードの例として、読む価値のある 全体的に、私はこの記事があまり好きではありませんでした。その理由は、TS ではジェネリックが広く使用されているものの、その独自の設計により、プレイアビリティが低いためです。しかし、私はこの設計コンセプトを支持します。まず、型を定義するための要件を満たすことができます。次に、C++ テンプレートよりもシンプルで使いやすいです。 ジェネリックに関する C++ TypeScript シリーズの記事はこれで終わりです。TypeScript ジェネリックに関するその他の関連コンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
>>: Apache ソースコードのインストールと仮想ホストの設定に関する詳細なチュートリアル
1. 50と93では鏡像が消える [root@h50 /]# df -h ファイルシステムの使用済み...
MySQLのパーティショニングは、非常に大きなテーブルを管理するのに役立ちます。MySQLのパーティ...
1.html部分コードをコピーコードは次のとおりです。 <!DOCTYPE html> ...
目次MyISAM と InnoDBパフォーマンスの低下と SQL の速度低下の理由: MySQL 実...
目次序文ストアドプロシージャ: 1. ストアドプロシージャの作成と呼び出し1. ストアドプロシージャ...
目次MySQL の 4 つの分離レベルデータ テーブルを作成します。分離レベルの設定物事の分離レベル...
1. レンダリング2. 操作手順1. テンセントマップキーを申請する - 住所2. ミニプログラムの...
導入MySQL の SQL クエリ ステートメントで is null、is not null、!= ...
1 はじめにKong は単純な製品ではありません。この記事で言及されている Kong は主に Kon...
コンピュータに初めて MySQL をインストールする場合、通常このエラー メッセージは表示されません...
ライフサイクル分類vue の各コンポーネントは独立しており、各コンポーネントには独自のライフサイクル...
効果:スライドショーが一方向に動く場合、各画像のサイズ、位置、透明度、レベルを変更する必要があります...
目次序文配列.プロトタイプ.includes文法パラメータ戻り値例配列プロトタイプの削減文法パラメー...
この記事では、例を挙げて mysql show 操作について説明します。ご参考までに、詳細は以下の通...
Linux と Unix はマルチユーザー オペレーティング システムであるため、ファイルの権限と所...