带有 React-Navigation 的类型安全 navigationHandler

ent*_*ull 3 typescript react-native react-navigation

我的 React Native 应用程序的 Typescript 和 React Navigation 存在问题。我的项目中有以下设置,这在使用例如时为我提供了很棒的帮助和自动完成功能navigation.push()

类型

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

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

export type StackNavigationProps = StackNavigationProp<RootStackParamList>;
Run Code Online (Sandbox Code Playgroud)

用法

const navigation = useNavigation<StackNavigationProps>();
Run Code Online (Sandbox Code Playgroud)

但是,我创建了一个导航处理程序函数,该函数接受一个被馈送到navigation.push. 只要参数的类型是 type ,它就可以正常工作any

const navigationHandler = useCallback((targetScreen) => {
    navigation.push(targetScreen);
}, [navigation]);
Run Code Online (Sandbox Code Playgroud)

但我对这种类型并不满意,any并且希望它targetScreen实际上只是我在其中声明的屏幕之一RootStackParamList类型中声明的屏幕之一。我尝试了无数种方法来满足push导航对象的方法,但我被困住了。

以下尝试是最成功的,但尚未完全发挥应有的作用。它确实让我在可用屏幕上自动完成,但是push仍然不满意。

类型:

export type NavigationScreen<T extends RootStackParamList> = {
    [K in keyof T]: K;
}[keyof T];
Run Code Online (Sandbox Code Playgroud)

用法:

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

将鼠标悬停在错误上时,我会收到以下提示:

(参数) targetScreen: { [K in keyof T]: K; }[keyof T] 类型参数 '[{ [K in keyof T]: K; }[keyof T]]' 不可分配给类型为 '["Home" | 的参数 “内容”| 「清单」]| [“家”| “内容”| “列表”,未定义]'。输入 '[{ [K in keyof T]: K; }[keyof T]]' 不可分配给类型 '["Home" | “内容”| “列表”]'。
类型“keyof T”不可分配给类型““Home”| “内容” | “列表”'。
输入“字符串|” 数量 | 符号' 不可分配给类型'"Home" | “内容”| “列表”'。
类型“string”不可分配给类型“Home” | “内容” | “列表”'。
类型“keyof T”不可分配给类型““List””。
输入“字符串|” 数量 | “符号”不可分配给类型““列表””。
类型“string”不可分配给类型““List””。

如何让它发挥作用?

Zwe*_*der 6

如果您只想为您的函数使用屏幕名称navigationHandler而不是参数,您可以按照 @artcorpse 在他对您的问题的评论中建议的方式进行操作并使用keyof RootStackParamList

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

另一方面,如果您想允许参数,您有两个选择:

选项 A:通用函数

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

此选项的优点是根据类型中定义的相应路线检查路线参数对象形状RootStackParamList。例如,

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

navigationHandler('Home', {a: 'a'}); //OK
navigationHandler('Home', {userId: 'a'}); //ERROR
Run Code Online (Sandbox Code Playgroud)

选项 B:反转已经存在的类型

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

此选项采用您已定义的现有类型并尝试从方法中提取它们navigation.push。这里的缺点是您将无法获得上述有关路线及其相应参数对象形状的检查。

资料来源: