Redux的输入/像装饰器一样连接

时间:2018-06-26 11:18:33

标签: reactjs typescript generics

我正在尝试创建一个行为与redux的connect装饰器类似的装饰器:在装饰的组件中注入一些额外的props。我在输入文字时遇到问题

extra.d.ts

export interface ExtraProps {
  extra: string;
}

// problem must be with this. Intent is to "add" the "extra" prop
// and not require it when the user actually renders it.
export function extra<P>(): (
  Comp: ComponentType<P & ExtraProps>
) => ComponentType<P>;

sample.tsx

interface MyCompProps extends ExtraProps {
  notSoExtra: string;
}

class MyComp extends React.Component<MyCompProps> {
  render() {
    console.log(this.props.extra, this.props.notSoExtra); // ok
    return null;
  }
}

// Decorating it here, prop types should be { notSoExtra: string } 
const MyCompWithExtra = extra()(MyComp);

class App extends React.Component {
  render() {
    return <MyCompWithExtra notSoExtra="boring prop" />;
    // error ^^^^^^
  }
}

因此,在渲染MyCompWithExtra时出现错误:

  

[ts]键入'{notSoExtra:string; }'与   输入'IntrinsicAttributes&{children ?: ReactNode; }'。

另外,当我像这样明确指定道具类型时:

const MyCompWithExtra = extra<MyCompProps>()(MyComp);

我在渲染时遇到了另一个错误

  

[ts]键入'{notSoExtra:string; }'无法分配给type   'IntrinsicAttributes&MyCompProps&{children ?: ReactNode; }'。
  输入'{notSoExtra:string; }'无法分配给type   “ MyCompProps”。       类型'{notSoExtra:string;类型中缺少属性'extra'。 }'。

如何键入装饰器,以便在渲染装饰组件时可以省略道具?

1 个答案:

答案 0 :(得分:1)

第一个问题是,如果您在P函数中指定extra参数,而将在返回的函数调用中指定推断它所需的信息,则编译器将不会能够推断P

您可以使用单个函数,或者,如果要指定其他参数,则可以从其他函数返回通用函数。

但是,更大的问题是P & ExtraProps并不意味着ExtraProps中的属性将不包含在P中。 P将包含所有属性,其中& ExtraProps会更多地限制这些属性的类型。

要创建从其他类型中排除属性的类型,可以使用Pick组合来选择类型中的特定属性,并使用Exclude<keyof A, keyof B>组合从列表中排除B的键。 A的键。

export interface ExtraProps {
    extra: string;
}
export function extra<P extends ExtraProps>(
    Comp: ComponentType<P>
) : ComponentType<Pick<P, Exclude<keyof P, keyof ExtraProps>>> {
    return null as any;
}

//Usage
interface MyCompProps extends ExtraProps {
    notSoExtra: string;
}

class MyComp extends React.Component<MyCompProps> {
    render() {
        console.log(this.props.extra, this.props.notSoExtra); // ok
        return null;
    }
}
const MyCompWithExtra = extra(MyComp);
let s = <MyCompWithExtra notSoExtra="boring prop" />;

注意:如果要允许用户选择指定extra,则可以返回ComponentType<Pick<P, Exclude<keyof P, keyof ExtraProps>>> & Partial<ExtraProps>