Heim > Web-Frontend > js-Tutorial > Eindeutige Symbole: So verwenden Sie Symbole für die Typensicherheit

Eindeutige Symbole: So verwenden Sie Symbole für die Typensicherheit

Mary-Kate Olsen
Freigeben: 2025-01-16 10:48:09
Original
503 Leute haben es durchsucht

Unique Symbols: How to Use Symbols for Type Safety

Wenn Sie einige Zeit mit React gearbeitet haben, sind Sie möglicherweise auf die Funktion queryOptions() von React Query gestoßen. Die Umsetzung sieht erschreckend einfach aus:

export function queryOptions(options: unknown) {
  return options
}
Nach dem Login kopieren

Der wahre Zauber liegt jedoch in den überladenen Funktionssignaturen. Was ist also das Besondere daran?

Wenn Sie sich nicht sicher sind, was eine überladene Funktion ist, können Sie sich diesen Beitrag ansehen: Funktionsüberladung: Umgang mit mehreren Funktionssignaturen

Typisierte Datenbankabfragen

Inspiriert durch den Ansatz von React Query habe ich eine Hilfsfunktion zusammengestellt, die für Leute, die außerhalb von React arbeiten, nützlich sein könnte: eine unkomplizierte Möglichkeit, typisierte Abfragen zu erstellen, zum Beispiel SQL-Abfragen.

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>;
}
Nach dem Login kopieren

Ähnlich wie queryOptions() ist die Funktion selbst ziemlich langweilig: Sie nimmt eine SQL-Anweisung entgegen, verpackt sie in ein Objekt vom Typ Query und gibt sie zurück.

Hier ist ein kurzes Beispiel dafür, wie Sie es nennen würden:

const getUserById = query<{ id: number }, { name: string; email: string }>(
  'SELECT name, email FROM users WHERE id = $id',
);
Nach dem Login kopieren

Beachten Sie, wie wir zwei Typen als generische Parameter übergeben. Der erste sind die erforderlichen Abfrageparameter – in diesem Fall die ID. Die zweite stellt den Rückgabetyp der Abfrage dar – Name und E-Mail.

Unter der Haube bettet query() diese beiden Typen in das zurückgegebene Objekt ein und speichert sie in queryParamsSymbol und queryReturnSymbol. Diese Symbole werden als eindeutige Symbole deklariert, was bedeutet, dass sie nur im Typraum existieren und nicht im transpilierten JavaScript erscheinen.

Ich verwende diese Symbole, um die Parameter- und Rückgabetypen vorübergehend zu speichern und sie bei Bedarf abzurufen.

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 }
Nach dem Login kopieren

InferQueryParams und InferQueryReturn sind lediglich Dienstprogrammtypen, um zu bestätigen, dass unsere Parameter- und Rückgabetypen korrekt abgeleitet werden. Sie brauchen sie vielleicht nicht wirklich, aber sie sind nützlich, um unseren Ansatz zu überprüfen.

Datenbank-Client

Da wir nun wissen, wie man Parameter- und Rückgabetypen in ein Abfrageobjekt einbettet, wie führen wir diese Abfragen tatsächlich aus? Werfen wir einen Blick auf einen einfachen Datenbank-Client, der unsere typisierten Abfragen ausführen kann:

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);
Nach dem Login kopieren

In diesem Beispiel übergeben wir unser typisiertes getUserById-Abfrageobjekt an die db.execute()-Methode. Da der Abfragetyp sowohl Parameter- als auch Rückgabetypinformationen enthält, leitet TypeScript diese automatisch ab. Wir können dies leicht bestätigen, indem wir mit der Maus über das Ergebnis fahren. TypeScript schlägt außerdem die ID als Eigenschaft unseres Parameterobjekts vor.

TypeScript-Spielplatz

Das vollständige Codebeispiel finden Sie in diesem TypeScript Playground.

Das obige ist der detaillierte Inhalt vonEindeutige Symbole: So verwenden Sie Symbole für die Typensicherheit. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage