如何让TypeScript函数兼容Redux连接的Screen派生类?
这个问题的核心在于:Redux的connect函数返回的高阶组件(HOC)默认不会保留原组件的静态属性类型信息。你的wrapStackNavigator函数原本约束参数必须是typeof Screen的子类,但被connect包装后的组件类型已经脱离了这个继承链,导致TypeScript无法识别它拥有identifier静态属性。
下面是几种优雅的解决方案,完全符合你的类型安全要求(不使用any):
方案1:放宽wrapStackNavigator的类型约束,基于属性而非继承
与其要求参数必须继承自Screen类,不如直接约束参数必须拥有identifier静态属性,且是合法的React组件构造函数。这种方式更灵活,不管组件是否被connect包装,只要满足这两个条件就能通过类型检查。
import React, { Component } from 'react'; import { StackNavigator } from '@react-navigation/stack'; // 定义通用类型:带有identifier静态属性的React组件构造函数 type ScreenWithIdentifier<P = {}, S = {}, SS = {}> = { readonly identifier: string; // 匹配静态getter的readonly特性 new(props: P): Component<P, S, SS>; }; export default function wrapStackNavigator<T extends ScreenWithIdentifier>(screen: T) { return ( <Stack.Navigator> <Stack.Screen name={screen.identifier} component={screen} /> </Stack.Navigator> ); }
这样修改后,不管是原始的HomeScreen,还是被connect包装后的组件(运行时connect会自动复制原组件静态属性),都能正常传入wrapStackNavigator。
方案2:让connect保留原组件的静态属性类型
如果你希望严格保留Screen类的继承关系,可以手动给connect的返回类型合并原组件的静态属性。我们可以定义一个工具类型来简化这个操作:
import { connect, ConnectedComponent } from 'react-redux'; import Screen from './Screen'; // 工具类型:合并原组件的静态属性到connect返回的组件类型 type ConnectedScreen<T extends typeof Screen> = ConnectedComponent<T, {}> & Pick<T, keyof T>; // 提取原Screen子类的所有静态属性(包括identifier) // 包装HomeScreen并指定类型 const ConnectedHomeScreen = connect(mapStateToProps, mapDispatchToProps)(HomeScreen); export default ConnectedHomeScreen as ConnectedScreen<typeof HomeScreen>;
现在导出的ConnectedHomeScreen会被TypeScript识别为既具有connect包装后的组件特性,又保留了原HomeScreen的静态identifier属性,完美兼容wrapStackNavigator的原有类型约束。
方案3:类型断言快速修复(适合临时场景)
如果你只是想快速解决当前问题,不想修改太多代码,可以在传入wrapStackNavigator时使用类型断言,明确告诉TypeScript这个包装后的组件拥有identifier属性:
// 假设ConnectedHomeScreen是connect包装后的组件 wrapStackNavigator(ConnectedHomeScreen as typeof HomeScreen);
不过这种方式依赖手动断言,类型安全性不如前两种方案,仅建议在临时场景使用。
额外提示
运行时connect默认会复制原组件的静态属性到包装后的组件上(除非你在connect的options里设置了特殊配置),所以实际运行时是能访问到identifier的,只是TypeScript需要明确的类型提示来识别它。
内容的提问来源于stack exchange,提问作者Can Poyrazoğlu

