如果您花了一些時間使用 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', );
注意我們如何將兩種類型作為泛型參數傳遞。第一個是必要的查詢參數 — 在本例中為 id。第二個代表查詢的回傳類型 - 姓名和電子郵件。
在底層,query() 將這兩種類型嵌入到傳回的物件中,並將它們儲存在 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中文網其他相關文章!