首页 > 解决方案 > redux 的类型 / 像装饰器一样连接

问题描述

我正在尝试创建一个装饰器,其行为类似于 redux 的connect装饰器:在装饰组件中注入一些额外的道具。我的打字有问题

额外的.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>;

样本.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; }' 不可分配给类型 'IntrinsicAttributes & MyCompProps & { children?: ReactNode; }'。
键入'{ notSoExtra:字符串;}' 不可分配给类型 'MyCompProps'。'{ notSoExtra: string; 类型中缺少属性 'extra' }'。

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

标签: reactjstypescriptgenerics

解决方案


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

您可以使用单个函数,或者,如果您想指定其他参数,您可以从 extra 返回一个泛型函数。

但更大的问题是,这P & ExtraProps并不意味着 in 中的属性ExtraProps不会包含在P. P将包含所有属性,并且& ExtraProps更多地作为对这些属性类型的约束。

要创建从另一种类型中排除属性的类型,您可以使用 的组合Pick从类型中选择特定属性,并从 的键中Exclude<keyof A, keyof B>排除 的键。BA

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>改为返回。


推荐阅读