Website-Suche

TypeScript - Typensicherer NavigationHandler mit React-Navigation


Ich habe ein Problem mit Typescript und React Navigation für meine React Native App. Ich habe die folgende Einrichtung in meinem Projekt, die mir großartige Hilfe und Autovervollständigung gibt, wenn ich z. B. navigation.push() benutze.

Arten

import { StackNavigationProp } from '@react-navigation/stack';

export type RootStackParamList = {
    Home: undefined;
    Content: undefined;
    List: undefined;
};

export type StackNavigationProps = StackNavigationProp<RootStackParamList>;

Verwendung

const navigation = useNavigation<StackNavigationProps>();

Ich habe jedoch eine Navigations-Handler-Funktion erstellt, die einen Parameter übernimmt, der an navigation.push übergeben wird. Sie funktioniert einwandfrei, solange der Typ des Parameters vom Typ any ist.

const navigationHandler = useCallback((targetScreen) => {
    navigation.push(targetScreen);
}, [navigation]);

Aber ich bin mit dem any-Typ nicht zufrieden und würde es bevorzugen, dass targetScreen tatsächlich nur einer der Bildschirme sein könnte, die in meinem RootStackParamList-Typ deklariert wurden. Ich habe auf unzählige Weisen versucht, die push-Methode des Navigationsobjekts zufriedenzustellen, aber ich stecke fest.

Der folgende Versuch war der erfolgreichste, funktioniert jedoch noch nicht genau so, wie er sollte. Er gibt mir zwar die Autovervollständigung für verfügbare Bildschirme, aber das push ist immer noch unzufrieden.

Typ :

export type NavigationScreen<T extends RootStackParamList> = {
    [K in keyof T]: K;
}[keyof T];

Verwendung :

const navigationHandler = useCallback(
    <T extends RootStackParamList>(targetScreen: NavigationScreen<T>) => {
        navigation.push(targetScreen);
},[navigation]);

Wenn ich über den Fehler fahre, werde ich mit Folgendem aufgefordert:

(Parameter) targetScreen: { [K in keyof T]: K; }[keyof T] Argument des Typs '[{ [K in keyof T]: K; }[keyof T]]' kann nicht dem Parameter des Typs '["Home" | "Content" | "List"] | ["Home" | "Content" | "List", undefined]' zugewiesen werden. Typ ' [{ [K in keyof T]: K; }[keyof T]]' kann nicht dem Typ '["Home" | "Content" | "List"]' zugewiesen werden.
Typ 'keyof T' kann nicht dem Typ '"Home" | "Content" | "List"' zugewiesen werden.
Typ 'string | number | symbol' kann nicht dem Typ '"Home" | "Content" | "List"' zugewiesen werden.
Typ 'string' kann nicht dem Typ '"Home" | "Content" | "List"' zugewiesen werden.
Typ 'keyof T' kann nicht dem Typ '"List"' zugewiesen werden.
Typ 'string | number | symbol' kann nicht dem Typ '"List"' zugewiesen werden.
Typ 'string' kann nicht dem Typ '"List"' zugewiesen werden.

Wie bekommt man das zum Laufen?

Antwort #1

Wenn Sie nur daran interessiert sind, den Namen des Bildschirms für Ihre navigationHandler-Funktion zu verwenden, aber nicht die Parameter, können Sie wie @artcorpse in seinem Kommentar zu Ihrer Frage vorgeschlagen hat, keyof RootStackParamList verwenden:

const navigationHandler = useCallback(
    (targetScreen: keyof RootStackParamList) => {
        navigation.push(targetScreen);
    },
    [navigation]
);

Wenn Sie andererseits Parameter zulassen möchten, haben Sie zwei Möglichkeiten:

Option A: Generische Funktion

const navigationHandler = useCallback(
    <T extends keyof RootStackParamList>(
        targetScreen: T,
        targetScreenParams?: RootStackParamList[T]
    ) => {
        navigation.push(
            targetScreen as keyof RootStackParamList,
            targetScreenParams
        );
    },
    [navigation]
);

Der Vorteil dieser Option besteht darin, dass die Formen der Routenparameterobjekte gegen die entsprechenden Routen überprüft werden, wie sie im RootStackParamList-Typ definiert sind. Zum Beispiel,

type RootStackParamList = {
    Default: undefined,
    Home: {a: 'a'};
    Profile: { userId: string };
};

navigationHandler('Home', {a: 'a'}); //OK
navigationHandler('Home', {userId: 'a'}); //ERROR

Option B: Vorhandene Typen umkehren

const navigationHandler2 = useCallback(
    (
        targetScreen: Parameters<typeof navigation.push>[0],
        targetScreenParams?: Parameters<typeof navigation.push>[1]
    ) => {
        navigation.push(targetScreen, targetScreenParams);
    },
    [navigation]
);

Diese Option nimmt die vorhandenen Typen, die Sie definiert haben, und versucht, sie aus der navigation.push-Methode zu extrahieren. Der Nachteil hierbei ist, dass Sie die oben erwähnte Überprüfung bezüglich der Routen und ihrer entsprechenden Parameterobjektstrukturen nicht erhalten.

Quellen :

  • API-Dokumentation für navigation.push
  • Typescript-Empfehlungen für React-Navigation

Verwandte Artikel