Conversation with Merlin [email protected] · Thu Nov 30 2023

TypeSafe とより具体的な型指定例のための TypeScript でのジェネリック型パラメーターと型注釈の使用

関数シグネチャの一般化:

  • 2 つの関数が異なる型で動作するという問題は、ジェネリックを使用した一般化された動作によって解決されます。
  • このシナリオには、入力パラメーターの型のみが異なる、同様の型ガード関数を持つ言語ファイルとメディア データが含まれます。

関連する関数の引数の作成:

  • 依存パラメーターを持つ関数は、ジェネリック型と制約によって処理され、適切な型の動作が保証されます。
  • リストから特定のエントリをロードする関数 fetchFile の例が示されており、型の安全性を確保する一般的な制約が示されています。

タプル型の使用:

  • 最初の要素として言語キー、2 番目の要素としてデータを使用して、結果をタプルに保存します。
  • 結果にタプル型の注釈を付けることで、戻り値からより多くの情報が得られる方法。

関係性のある絞り込みタイプ:

  • ジェネリック型パラメーター間の関係を設定して型を絞り込みます。
  • keyof List をサブタイプ 'de' に置き換えます。 「fr」とこの置換の意味。

汎用インスタンス化:

  • 使用法により、明示的および暗黙的に型パラメータを実際の値に置き換えます。
  • ジェネリック型パラメーターを使用して、適切なタイミングで適切な型情報を取得します。

未知のものを取り除く:

  • ジェネリック型パラメーター (any、unknown) を使用する場合。
  • 実際の型が最終的に判明している場合は、ジェネリック型パラメーターを使用します。

ジェネリックインスタンス化を理解する:

  • ジェネリックが実際の型をどのように置き換えるかを理解します。
  • よくある混乱点の例と説明を明確にします。

汎用関数:

  • ジェネリック型パラメーターを持つ関数を使用して、任意の型の値を処理し、型を絞り込みます。
  • 関数が特定の型で動作することを保証するためにジェネリック型パラメーターを使用する場合。

ジェネリック医薬品のメリット:

  • ジェネリックを使用すると、コード内でより詳細かつ具体的な入力が可能になる方法。
  • ジェネリックを使用してサブタイプ間の互換性を確保し、コードの信頼性を高めます。

ジェネリック医薬品を使用する場合:

  • 特定の型情報の必要性に基づいて、いつジェネリックを使用するかを決定します。
  • より広範な値のセットとサブタイプの互換性と比較して、ジェネリックの必要性を評価します。

フィルタロジックの作成:

  • さまざまなフィルター ルールとコンビネータを使用してアプリケーション用のフィルター ロジックを作成しました
  • フィルターの動作に基づいて定義されたタイプ

リセット機能:

  • 提供されたフィルターに基づいてフィルタールールをリセットするリセット機能を実装しました
  • CombinatorialFilter と ChainedFilter を区別するために型ガードを使用しました

サブタイプに優しいコード:

  • サブタイプに優しいコードの必要性を特定
  • 入力オブジェクトのクローンを利用し、それに応じてプロパティを設定して型の一貫性を維持します。

共用体型のジェネリック型パラメータ:

  • TypeScript での広範な共用体型の実装に関する実証済みの問題
  • 問題に対処するためにフィルターに制約されたジェネリック型パラメーターを利用しました

新しいオブジェクトタイプの生成:

  • 元の型に基づいて新しいオブジェクト型を作成する汎用マップ型を使用して導入されました。
  • 新しいオブジェクト型を作成するために汎用のマップされた型を使用する実際の例を示します。

グループのタイプアノテーション:

  • カテゴリが忘れられないように、グループを宣言するときに明示的な型アノテーションが使用されます。
  • GroupedToys のキーは Toy の「kind」タイプの結合と一致するため、インデックスに簡単にアクセスできます。

おもちゃにレンガの種類を追加する:

  • 連動するおもちゃのレンガを表す Bricks タイプが Toy モデルに追加されます。
  • 新しい Bricks タイプを処理するには、groupToys 関数を更新する必要があります。

groupToys機能のアップデート:

  • groupToys 関数は、新しい Bricks タイプを処理し、暗黙的なタイプを回避するように更新されました。
  • TypeScript の動作には、型が一致しない場合の認識が含まれるようになりました。

おもちゃのグループ化の最適化:

  • GroupedToys タイプは、オプションのプロパティを備え、使用できない場合は空の配列で各グループを初期化するように変更されました。
  • この最適化は、変更のたびに groupToy を維持する必要性を回避することを目的としています。

グループ化パターンの一般化:

  • ジェネリック型 Group は、コレクションを取得し、特定のセレクターによってグループ化するために作成されます。
  • Group タイプでは、コレクションおよびセレクター タイプのパラメーターを指定して GroupedToys オブジェクトを作成できます。

アサーション署名を使用したオブジェクトの変更:

  • アサーション シグネチャを使用して、if ステートメントや switch ステートメントとは独立して型を変更します。
  • アサーション シグネチャは、特定の関数の実行後に型を変更する場合に効果的です。

型アサーションとアサート関数:

  • (obj as T & {checked: boolean }).checked = true; のようなアサーションを入力します。 TypeScript では、条件を必要とせずに値の型を変更するために使用されます。
  • アサーション シグネチャと呼ばれる別の手法を使用すると、条件を必要とせずに制御フロー内の値の型を変更できます。

アサーション署名の使用:

  • アサーション シグネチャを使用すると、述語関数で true を返す必要がなく、より多くのプロパティが利用可能であることを TypeScript に伝えることができます。
  • アサーション署名を使用すると、条件ステートメントを必要とせずに、値の型をその場で変更できます。

型マップを使用した型のマッピング:

  • タイプ マップを利用すると、TypeScript の要素タグ名とプロトタイプ オブジェクト間のマッピングを提供できます。
  • Partial などのマップされた型を使用すると、特定の HTML 要素バリアントのすべてのプロパティをオプションにすることができます。

ファクトリ関数と型マップ:

  • ファクトリ関数を使用すると、型マップや Partial などのマップされた型を利用して、文字列識別子に基づいて特定のサブタイプのオブジェクトを作成できます。
  • HTMLElementTagNameMap タイプを使用すると、HTML 要素が有効であることを確認し、HTML 要素に追加のプロパティを渡すオプションを提供できます。

HTMLElementTagNameMap の拡張:

  • HTMLElementTagNameMap インターフェイスはインデックス付き署名で拡張できます
  • マップされた型を使用すると、createElement 関数で正しい要素名を確保できます。

ThisType を使用してオブジェクト内で this を定義する:

  • このタイプ<T>メソッドを使用して、複雑な構成オブジェクトで正しい「this」コンテキストを定義するために使用できます。
  • オプションのタイプを調整して、正しい「this」コンテキストでデータ、計算、およびメソッドのプロパティを指定できます

問題:

  • TypeScript は、複雑なリテラル値を関数に渡すときに型をより一般的なものに拡張しますが、これは必ずしも望ましい動作ではありません。
  • 場合によっては、拡張型ではなくリテラル型に取り組む必要があります。

解決:

  • 渡された値を const コンテキストに保持するには、ジェネリック型パラメーターの前に const 修飾子を追加します。

議論:

  • SPA フレームワークは、通常のナビゲーション動作をオーバーライドできる History API など、多くのブラウザー機能を JavaScript で再実装する傾向があります。
  • Route に T を導入し、文字列の代わりに T["path"] を使用すると、目的の動作を実現するのに役立ちます。
  • 入力パラメータを読み取り専用として定義し、const コンテキストを手動で適用することは、型の制限を拡大するための回避策です。
  • TypeScript を使用すると、ジェネリック型パラメーターから const コンテキストをリクエストできるようになり、適切なエラーとより明確なインターフェイス制約が提供されます。

解説する

TypeScriptを使用する際、特定の文字列リテラルをリテルタイプとして固定し、型マップから正しいHTML要素のバリアントをインデックスで参照するためにジェネリクスを使用することができます。また、document.createElementを使用する際は、2つの型アサーション(型指定の強制)が必要です。1つはセットを広げるためのものです(Tを文字列に)、もう1つはセットを狭めるためのものです(HTMLElementをAllElements[T]に)。これらのアサーションは、第3.9レシピにおいて確立されたように、制御外のAPIを扱っていることを示しています。これらのアサーションについては後ほど扱います。

さらに、HTMLAnchorElementにhrefを設定するなど、指定したHTML要素に追加のプロパティを渡すオプションを提供したいと思います。これらのプロパティはすでに各HTMLElementバリアンツに存在しますが、必須ではなくオプショナルにする必要があります。既存の型のすべてのプロパティを任意のものとするには、組み込み型のPartial<T>を使います。これはマップされた型で、特定の型の全てのプロパティを取得し、そこに型修飾子を追加します。

対案を考える。

ファクトリ関数は、基本的な情報に基づいて複雑なオブジェクトのバリアントを作成したい場合に適しています。ブラウザのJavaScriptで知られている一つのシナリオは、要素の作成です。document.createElement関数は要素のタグ名を受け取り、必要なすべてのプロパティを変更できるオブジェクトを返します。

ここで、createElementという整ったファクトリ関数を作成して、この作成プロセスをもっと手軽にします。この関数は要素のタグ名を取るだけでなく、個々のプロパティを一つひとつ設定する必要がないように、プロパティのリストも作成します:

function createElement<K extends keyof HTMLElementTagNameMap>( tagName: K, props?: Partial<HTMLElementTagNameMap[K]> ): HTMLElementTagNameMap[K] { const element = document.createElement(tagName) as HTMLElementTagNameMap[K]; if (props) { Object.keys(props).forEach(key => { element[key] = props[key]; }); } return element; } // 使用例 const anchor = createElement('a', { href: 'https://www.example.com', target: '_blank' }); document.body.appendChild(anchor``` この関数はジェネリクスKを使用しており、`HTMLElementTagNameMap`からタグ名に対応する要素タイプを導出しています。`props?`引数はオプショナルなので、設定したいプロパティだけを渡すことができます。各プロパティは`Partial`型を使って任意になっており、必要なものだけを含めることができるようになります。ファクトリ関数を使用することで、要素の作成と設定が、より宣言的で読みやすく、そして使いやすくなります。

解説する

ファクトリ関数は、いくつかの基本情報に基づいて複雑なオブジェクトの異なるバリエーションを作成したい場合に非常に適しています。ブラウザJavaScriptでよく知られているのは、要素の生成のシナリオです。document.createElement 関数は、要素のタグ名を引数にとり、必要なすべてのプロパティを変更できるオブジェクトを生成します。

この生成プロセスをさらに便利にするために、createEl⁠ement と呼ばれる洗練されたファクトリ関数を作りたいとします。この関数は要素のタグ名だけでなく、プロパティのリストも受け取るので、各プパティを個々に設定する手間が省けます。

以下に簡単なコード例を示します。

function createElement<K extends keyof HTMLElementTagNameMap>( tagName: K, props?: Partial<HTMLElementTagNameMap[K]> ): HTMLElementTagNameMap[K] { // 指定されたタグ名で要素を作成 const element = document.createElement(tagName) as HTMLElementTagNameMap[K]; // オプショナルなpropsが提供された場合、それらのプロパティを要素に適用する if (props) { Object.keys(props).forEach(key => { (element as any)[key] = props[key]; }); } // 完成した要素を返す return element; } // 使用例 const anchor = createElement('a', { href: 'https://www.example.com', target: '_blank' }); document.body.appendChild(anchor);

この関数では、カスタムのHTML要素タイプを作成する際、ジェネリック型Kを使ってHTMLElementTagNameMapから対応する要素の型を引き出します。第二引数のpropsPartialを用いることで、これらのプロパティを任意とします。これにより、作成した要素に適用したいプロパティのみを提供することができます。

このcreateElementファクトリ関数を利用することで、要素の作成とプロパティ設定が宣言的で読みやすく、簡単に行えるようになります。