reactjs – 使用[key:string]:any以避免需要额外的泛型函数?

我在React中使用重构,特别是其具有签名的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);

(作为演示的一个愚蠢的例子).但问题是,我的组件有更多的道具,组成生成的HoC需要采取那些额外的道具并传递它们,例如:

interface IMyComponentProps {
    somethingYouGiveMe: string;
    somethingElse: string;
}

因此,我不能做const MyEnhancedComponent = myEnhancer(MyComponent),因为编译器会抱怨MyEnhancedComponent没有东西作为道具.

我发现了两个工作,我都不满意.好奇的是,经验丰富的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]:任何穷人都延伸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="" />
点赞