MofuMofu2 / typescript-sandbox

MIT License
0 stars 0 forks source link

#69 型安全なプロパティアクセス関数 #74

Closed MofuMofu2 closed 1 year ago

MofuMofu2 commented 1 year ago

resolve #69

MofuMofu2 commented 1 year ago

テストケースが理解できました。指定したプロパティの値を取得する関数accountPropertyを使用していますね。

次に、accountProperty関数の実装と、このテストケースで使用しているuserDataの定義を共有していただけると、より具体的なフィードバックをお伝えできます。

とはいえ、すでに共有いただいたテストケースだけを元に評価すると、次の点が挙げられます:

  1. describe文のタイトル:

    • describe('指定したユーザープロパティの値を返すことができる', () => {...});
    • このタイトルは明確で、何をテストしているのか理解しやすいです。
  2. 各テストケース:

    • 1つ目のテストケースは、posts以外のプロパティにアクセスすることを確認しており、適切です。
    • 2つ目のテストケースは、postsプロパティへのアクセスを確認しており、これも適切です。

このテストケースは、基本的なプロパティアクセスの機能をカバーしています。しかし、accountProperty関数の実装とuserDataの定義を確認しないと、完全なレビューはできません。これらのコードを共有してください。


データも一緒に送らないとうまく判定できないのか。ちょっとめんどい

MofuMofu2 commented 1 year ago

コードを確認しました。型安全なプロパティアクセス関数としては非常に良く書かれています。以下はいくつかのポイントとフィードバックです。

  1. ジェネリックを使用する: keyofを使用して、Accountオブジェクトのプロパティをタイプセーフにアクセスできるようにしています。これにより、関数を呼び出す際に不正なキーを指定するとTypeScriptのコンパイル時にエラーが発生します。

  2. 返り値の型: 返り値の型として、string | number | boolean | Post[] を指定しています。これは少し冗長であり、もう少し効率的に書くことができます。以下のようにして、propertyの型に基づいて返り値の型を動的に得ることができます。

    export const accountProperty = <T extends keyof Account>(
     account: Account,
     property: T
    ): Account[T] => {
     return account[property];
    };
  3. エラーハンドリング: 現在の実装では、存在しないプロパティにアクセスすると、TypeScriptのコンパイルエラーが発生します。しかし、ランタイム時のエラーハンドリングも検討する価値があります。たとえば、存在しないプロパティにアクセスした場合にはエラーメッセージを返すなどの処理を追加することが考えられます。

  4. テスト: すでに共有していただいたテストケースは良いと思います。accountProperty関数が正しく動作することを確認できる基本的なテストケースが網羅されています。

総合的に、非常によく書かれたコードだと思います。上記のフィードバックや提案を参考に、さらに改善してみてください!

MofuMofu2 commented 1 year ago

返り値の型:についてもう少し解説いただきたいです。ジェネリック型を使っているのはわかります。

了解しました。ジェネリックと、このコードでどのようにそれが動作するのかを説明します。

ジェネリック

ジェネリックは、TypeScriptの強力な機能の1つで、型をパラメータとして関数やクラスに渡すことができます。これにより、1つの関数やクラスでさまざまな型を操作できるようになります。基本的なジェネリック関数の例としては、以下のようなものが考えられます。

function identity<T>(arg: T): T {
    return arg;
}

上記の関数identityは、ジェネリック型Tを受け取り、同じ型Tを返します。この関数を使用すると、任意の型で関数を呼び出すことができます。

accountProperty関数のジェネリック

では、accountProperty関数のジェネリックについて詳しく見ていきましょう。

export const accountProperty = <T extends keyof Account>(
  account: Account,
  property: T
): Account[T] => {
  return account[property];
};

このジェネリックの利用により、関数は与えられたpropertyの型に基づいて返り値の型を動的に決定することができます。これにより、関数がより型安全になり、返り値の型を正確に示すことができます。

MofuMofu2 commented 1 year ago

どのような場面でジェネリック型を使うと判断すれば良いでしょうか?Union Typeとの使い分けが難しいです。

ジェネリック型を使用するかどうかの判断、およびUnion Typeとの使い分けは、しばしば開発の文脈やニーズに依存します。それでも、以下にいくつかの一般的な指針を示します。

ジェネリック型を使用する場面:

  1. 再利用可能なコンポーネント/関数: ジェネリックは特定の型に依存しない、再利用可能なコンポーネントや関数を作成するのに役立ちます。例えば、配列の最初の要素を返す関数を考えた場合、ジェネリックを使用することで、任意の型の配列に対応できる関数を作成することができます。

  2. 型の関係を保持: ジェネリックを使用すると、入力型と出力型の間の関係を保持することができます。例えば、上記のaccountProperty関数のように、特定のプロパティ名に基づいて返り値の型を動的に決定する場合に有効です。

  3. 高階関数: ジェネリックは、他の関数やクラスを引数として受け取ったり、返す関数やクラスを作成するときに特に有効です。

Union Typeを使用する場面:

  1. 制限された値のセット: 関数や変数が取りうる値の範囲が制限されている場合にUnion Typeを使用します。例: type Direction = "up" | "down" | "left" | "right";

  2. 異なる型の値を持つことが許可されている場合: 例えば、関数が文字列または数値を受け入れる場合、type StringOrNumber = string | number;のようなUnion Typeを使用できます。

ジェネリックとUnion Typeの使い分け:

  1. 柔軟性: ジェネリックは一般的に柔軟性が高く、再利用可能なコードを書くのに適しています。一方、Union Typeは特定の型の組み合わせに制限されるため、より具体的なシチュエーションでの使用に向いています。

  2. 明確さ: Union Typeは関数や変数が取り得る具体的な型を明示するのに役立ちます。これに対して、ジェネリックは一般的な形を持ち、その形に基づいて動的に型を決定します。

最終的には、特定の問題を解決するための最もシンプルで効果的な方法を選ぶことが大切です。適切なツールを選択することで、コードの可読性、保守性、および型安全性を向上させることができます。

MofuMofu2 commented 1 year ago

Union Typeとジェネリック型の使い分けをお題に出してもらうといいかもしれない。型があまりわかってなさそう。