reactjs - 使用 [key:string]: any 来避免需要额外的泛型函数?
问题描述
我在 React 中使用 recompose,特别是它的compose
函数,它的签名为
function compose<TInner, TOutter>(
...functions: Function[]
): ComponentEnhancer<TInner, TOutter>;
interface ComponentEnhancer<TInner, TOutter> {
(component: Component<TInner>): ComponentClass<TOutter>;
}
我在其中传递了一些 React 高阶组件。我将我的TInner
和TOuter
类型定义为接口,这些接口描述了我的 HoC 运行所需的内容:
interface IOuterProps {
somethingYouGiveMe: string;
}
interface IInnerProps {
somethingIGiveBack: number;
}
const myEnhancer = compose<IInnerProps, IOuterProps>(myHoC, myOtherHoc);
(作为演示的愚蠢示例)。但问题是,我的组件有更多的 props,compose 生成的 HoC 需要获取这些额外的 props 并传递它们,例如:
interface IMyComponentProps {
somethingYouGiveMe: string;
somethingElse: string;
}
因此,我不能这样做const MyEnhancedComponent = myEnhancer(MyComponent)
,因为编译器会抱怨MyEnhancedComponent
没有somethingElse
作为道具。
我找到了两个解决方法,我都不满意。很好奇更有经验的 TypeScript 开发人员会做什么。
解决方法#1:引入一个函数
通过引入一个函数,我可以使用泛型并向编译器表达我在做什么
function myEnhancer<TInnerProps extends IInnerProps, TOuterProps extends IOuterProps>(Component: React.ComponentType<TInnerProps>) {
return compose<TInnerProps, TOuterProps>(mYHoC, myOtherHoc)(Component);
}
我真的不喜欢这个,引入一个将创建增强器的多个副本的函数,只是为了获得泛型?对我来说似乎是错误的。
解决方法 #2:使用 [key:string]: any
我可以改为将我的界面更改为
interface IOutterProps {
somethingYouGiveMe: string;
[key: string]: any;
}
interface IInnerProps extends IOuterProps {
somethingIGiveBack: number;
}
我基本上[key:string]: any
用作穷人的extends IOutterProps
。比如,我需要你给我这些道具,如果你给我额外的东西,那么我真的不在乎那些。
这使我可以myEnhancer
在我拥有满足要求的组件的任何地方使用,避免添加的功能,并且感觉比解决方法 #1 更好。但是不得不添加也感觉不对[key: string]: any
。
解决方案
ComponentEnhancer
您可以在泛型内部制作函数签名:
declare function compose<TInner, TOutter>(
...functions: Function[]
): ComponentEnhancer<TInner, TOutter>;
interface ComponentEnhancer<TInner, TOutter> {
// This is now a generic function
<TActualInner extends TInner, TActualOuterProps extends TOutter>(component: React.ComponentClass<TActualInner>): React.ComponentClass<TActualOuterProps>;
}
interface IOuterProps {
somethingYouGiveMe: string;
}
interface IInnerProps {
somethingIGiveBack: number;
}
const myEnhancer = compose<IInnerProps, IOuterProps>();
interface IMyComponentProps {
somethingYouGiveMe: string;
somethingElse: string;
}
interface IMyInnerComponentProps {
somethingElse: string;
somethingIGiveBack: number;
}
class MyComponent extends React.Component<IMyInnerComponentProps>{ }
// We specify the actual inner and outer props
const MyEnhancedComponent = myEnhancer<IMyInnerComponentProps, IMyComponentProps>(MyComponent)
如果我们考虑转换如何工作的逻辑,我们甚至可以使用一些条件类型魔法来避免显式指定类型参数。如果我的理解compose
是正确的,那么结果组件将具有内部组件的所有属性,不包括 的属性IInnerProps
并包括 的属性IOuterProps
:
type Omit<T, TOmit> = { [P in Exclude<keyof T, keyof TOmit>] : T[P] }
type ComponentEnhancerProps<TActualInner, TInner, TOuter> = Omit<TActualInner, TInner> & TOuter;
interface ComponentEnhancer<TInner, TOutter> {
<TActualInner extends TInner>(component: React.ComponentClass<TActualInner>): React.ComponentClass<ComponentEnhancerProps<TActualInner, TInner, TOutter>>;
}
class MyComponent extends React.Component<IMyInnerComponentProps>{ }
const MyEnhancedComponent = myEnhancer(MyComponent)
let d = <MyEnhancedComponent somethingYouGiveMe="0" somethingElse="" />
这种自动化方法的问题是可选字段成为必需的,如果您已strictNullChecks
启用,您可以保持可选性
type ExcludeUndefined<T, TKeys extends keyof T> = { [P in TKeys]: undefined extends T[P] ? never : P}[TKeys];
type Omit<T, TOmit> =
{ [P in ExcludeUndefined<T, Exclude<keyof T, keyof TOmit>>] : T[P] } &
{ [P in Exclude<Exclude<keyof T, keyof TOmit>, ExcludeUndefined<T, Exclude<keyof T, keyof TOmit>>>] ? : T[P] }
type ComponentEnhancerProps<TActualInner, TInner, TOuter> = Omit<TActualInner, TInner> & TOuter;
interface ComponentEnhancer<TInner, TOutter> {
<TActualInner extends TInner>(component: React.ComponentClass<TActualInner>): React.ComponentClass<ComponentEnhancerProps<TActualInner, TInner, TOutter>>;
}
interface IMyInnerComponentProps {
somethingElse: string;
somethingIGiveBack: number;
optionalProp?: number;
}
class MyComponent extends React.Component<IMyInnerComponentProps>{ }
const MyEnhancedComponent = myEnhancer(MyComponent)
let d = <MyEnhancedComponent somethingYouGiveMe="0" somethingElse="" />
推荐阅读
- java - 关闭并重新打开 Android 应用程序后无法访问/控制正在进行的下载
- python - 'str' 对象在 Django 中没有属性 'user' 错误
- azure - 如何从 Azure 应用服务访问外部元数据库配置文件
- c# - 在 Office 加载项 C# 中,是否可以选择仅在 office 应用程序顶部打开表单?
- svelte - Svelte 编译器生成多个 JavaScript 类定义
- javascript - 节点 repl 中奇怪的(让)变量赋值行为 - 无法赋值
- java - 使用 AES CBC 在 perl 中正确格式化初始化向量
- django - 在 Django 的 ManyToManyFields 中自定义(样式)ModelMultipleChoiceField
- mysql - 如何在mysql中添加数值的百分比
- python - 替换颜色图中的颜色(python 3.7)