Heim >Web-Frontend >js-Tutorial >Eindeutige Symbole: So verwenden Sie Symbole für die Typensicherheit
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 }
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
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>; }
Ä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', );
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 }
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.
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);
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.
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!