首頁 > web前端 > js教程 > 獨特的符號:如何使用符號進行型別安全

獨特的符號:如何使用符號進行型別安全

Mary-Kate Olsen
發布: 2025-01-16 10:48:09
原創
470 人瀏覽過

Unique Symbols: How to Use Symbols for Type Safety

如果您花了一些時間使用 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 遊樂場

您可以在此 TypeScript Playground 中找到完整的程式碼範例。

以上是獨特的符號:如何使用符號進行型別安全的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板