React の作業に時間を費やしたことがある方は、React Query の queryOptions() 関数に遭遇したことがあるかもしれません。その実装は驚くほど単純に見えます:
export function queryOptions(options: unknown) { return options }
しかし、本当の魔法はオーバーロードされた関数シグネチャにあります。では、何がそんなに特別なのでしょうか?
オーバーロードされた関数が何であるかわからない場合は、次の投稿を参照してください: 関数のオーバーロード: 複数の関数シグネチャを処理する方法
React Query のアプローチに触発されて、React の外部で作業する人にとって役立つ可能性のあるヘルパー関数をまとめました。これは、SQL クエリなどの型指定されたクエリを作成する簡単な方法です。
export declare const queryParamsSymbol: unique symbol; export declare const queryReturnSymbol: unique symbol; export type Query< TParams extends Record<string, any> = Record<string, any>, TReturn extends Record<string, any> | undefined = undefined, TStatement extends string = string, > = { statement: TStatement; [queryParamsSymbol]: TParams; [queryReturnSymbol]: TReturn; }; export function query< TParams extends Record<string, any> = Record<string, any>, TReturn extends Record<string, any> | undefined = undefined, TStatement extends string = string, >(statement: TStatement): Query<TParams, TReturn> { return { statement: statement } as Query<TParams, TReturn, TStatement>; }
queryOptions() と同様に、関数自体は非常に鈍く、SQL ステートメントを受け取り、それを Query 型のオブジェクトにラップして返します。
これを呼び出す方法の簡単な例を次に示します。
const getUserById = query<{ id: number }, { name: string; email: string }>( 'SELECT name, email FROM users WHERE id = $id', );
2 つの型をジェネリック パラメーターとして渡す方法に注目してください。 1 つ目は必須のクエリ パラメーターです (この場合は id)。 2 番目は、クエリの戻り値の型 (名前と電子メール) を表します。
内部では、query() はこれら 2 つの型を返されたオブジェクトに埋め込み、queryParamsSymbol と queryReturnSymbol に隠します。これらのシンボルは一意のシンボルとして宣言されます。つまり、それらは型空間にのみ存在し、トランスパイルされた JavaScript には表示されません。
これらのシンボルを使用してパラメータと戻り値の型を一時的に保存し、必要なときにいつでも取得できます。
type InferQueryParams<TQuery> = TQuery extends Query<infer Params, any> ? Params : never; type UserQueryParams = InferQueryParams<typeof getUserById>; // ^? { id: number } type InferQueryReturn<TQuery> = TQuery extends Query<any, infer Return> ? Return : never; type UserQueryReturn = InferQueryReturn<typeof getUserById>; // ^? { name: string; email: string }
InferQueryParams と InferQueryReturn は、パラメーターと戻り値の型が正しく推論されていることを確認するための単なるユーティリティ型です。実際には必要ないかもしれませんが、アプローチを検証するのに便利です。
クエリ オブジェクトにパラメータと戻り値の型を埋め込む方法はわかったので、これらのクエリを実際に実行するにはどうすればよいでしょうか?型指定されたクエリを実行できる単純なデータベース クライアントを見てみましょう:
class DatabaseClient { async execute< TParams extends Record<string, any>, TReturn extends Record<string, any> >( query: Query<TParams, TReturn>, params: TParams, ): Promise<Array<TReturn>> { // execute the query and return the results // ... return []; } } const db = new DatabaseClient(); // Return type and parameters are inferred from the query object const result = await db.execute(getUserById, { id: 1 }); // ^? { id: number } // ^? Array<{ name: string; email: string }> console.log(result);
この例では、型指定された getUserById クエリ オブジェクトを db.execute() メソッドに渡します。 Query 型にはパラメーターと戻り値の型情報の両方が含まれるため、TypeScript はそれらを自動的に推測します。これは結果の上にマウスを置くことで簡単に確認でき、TypeScript はパラメータ オブジェクトのプロパティとして ID も提案します。
完全なコード例は、この TypeScript Playground にあります。
以上がユニークなシンボル: タイプセーフティのためのシンボルの使用方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。