React TypeScript 高阶组件类型
React TypeScript high order component typings
我正在尝试将现有的 React 组件 (react-select) 包装在高阶组件 (HoC) 中,以提供一些条件渲染逻辑。我面临的困难是让 TypeScript 生成一个组件,该组件结合了 HoC 包装器和 Wrapped 组件属性。
例如:
import React from "react";
import Select from "react-select";
interface HOCProps {
foo: string
}
// Return a type with the HOCProps properties removed.
type WitoutPrefilled<T extends HOCProps> = Pick<T, Exclude<keyof T, 'foo'>>;
function withHoC<P extends HOCProps>(WrappedComponent: React.ComponentType<P>) {
return class SomeHOC extends React.Component<WithoutPrefilled<P>> {
public render(): JSX.Element {
return <WrappedComponent {...this.props as P} /*foo={"test"}*/ />;
}
};
}
// Generate a wrapped component with a property union of the wrapped Select and outer HoC (HOCProps & Select) ?
const Wrapped = withHoC(Select);
完成此任务的正确方法是什么?
反应 16.8.3
打字稿 3.3.3
首先,确保您的高阶组件提供了您的组件感兴趣的道具。以className
为例。
interface WithClassName {
className: string;
}
我们的 withClassName
HOC 会接受一个准备好接受 className
作为 prop 的组件,return 一个不再接受 className
的组件。
export function withClassName<T extends React.ComponentType<Partial<WithClassName>>>(Component: T): React.FunctionComponent<Omit<React.ComponentProps<T>, keyof WithClassName>> {
return props => React.createElement(Component, { className: "foo", ...props });
}
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
用法:
const NewSelect = withClassName(Select);
好的 - 所以我已经设法让它工作了。当前的解决方案满足了我的两个关键要求:
推断 defaultProps
被包装的子组件,如果它们存在意味着由 HoC 包装器生成的组件不需要显式传递它们。
公开 HoC 包装器属性和底层子组件属性的联合,以便 Intellisense 显示包装组件上的所有可用属性。
在我试图简化实际发生的事情的过程中,withHoC
函数预期的类型是硬编码的(在我的例子中是 react-select
),因此 withHoC
包装器将只接受 react-select
Select
组件进行包装。其他任何东西都可能会引发类型错误。
This link 描述了一些代码,这些代码可能能够自动推断出要被 withHoC
包装的组件类型,从而使 withHoC
可以与 [=19 以外的组件类型一起重用=] Select
。
// node dependencies used (because dependencies mutate so much this may not work in other versions):
// "react": "^16.8.2",
// "react-dom": "^16.8.2",
// "typescript": "^3.3.3",
// "react-select": "2.4.1",
// "@types/react": "^16.8.3",
// "@types/react-dom": "^16.8.2",
// "@types/react-select": "^2.0.13",
// Visual Studio 2017 TypeScript SDK build 3.3.3
import ReactDOM from "react-dom"; // (Optional - just for testing)
import React from "react";
import Select from "react-select";
// Properties shape for React Select (See react-select @type definitions)
import { Props } from "react-select/lib/Select";
// The properties we are want to add so that our resultant wrapped component contains all of its own properties plus the extra properties specified here
interface IHOCProps {
bar: string;
}
function withHoC(WrappedComponent: React.ComponentType<Props>) {
return class SomeHOC extends React.Component<IHOCProps & Props> {
// If 'bar' isn't specified, configure a default (this section is optional)
static defaultProps = {
bar: "default bar"
};
public render(): JSX.Element {
return <><div>{this.props.bar}</div><WrappedComponent {...this.props as any} /></>;
}
};
}
const WrappedSelect = withHoC(Select);
export { WrappedSelect, Select };
// Test it out (Optional). If using Visual Studio 2017 or some other IDE with intellisense,
// <WrappedSelect /> should show all the 'react-select' properties and the HoC property (bar).
// Additionally, all the defaultProps for 'react-select' are automatically inferred so no TypeScript errors about missing props when using <WrappedSelect />.
const TestMe = () =>
<>
<WrappedSelect bar="bumble monkey">
<WrappedSelect />
<Select />
</>;
// Append the result to an HTML document body element
ReactDOM.render(<TestMe />,document.getElementsByTagName("body")[0]);
有些代价高昂的胜利,因为它不能跨类型重复使用,但它确实有效。
最后一个金块;如果您使用 Visual Studio 2017 和 Node for TypeScript,请确保 TypeScript SDK 版本与您的 npm 节点包同步,否则 IDE 会报告在执行命令行时不会自行显示的错误编译(这给我带来了无穷无尽的红鲱鱼问题)。
Microsoft 发布了 this Url,它很少更新并且可能已过时,但公司中没有人注意到。
总是可以在 GitHub here 上找到最新的 SDK,它通常总是与 npm 和 nuget 包一起发布。
我正在尝试将现有的 React 组件 (react-select) 包装在高阶组件 (HoC) 中,以提供一些条件渲染逻辑。我面临的困难是让 TypeScript 生成一个组件,该组件结合了 HoC 包装器和 Wrapped 组件属性。
例如:
import React from "react";
import Select from "react-select";
interface HOCProps {
foo: string
}
// Return a type with the HOCProps properties removed.
type WitoutPrefilled<T extends HOCProps> = Pick<T, Exclude<keyof T, 'foo'>>;
function withHoC<P extends HOCProps>(WrappedComponent: React.ComponentType<P>) {
return class SomeHOC extends React.Component<WithoutPrefilled<P>> {
public render(): JSX.Element {
return <WrappedComponent {...this.props as P} /*foo={"test"}*/ />;
}
};
}
// Generate a wrapped component with a property union of the wrapped Select and outer HoC (HOCProps & Select) ?
const Wrapped = withHoC(Select);
完成此任务的正确方法是什么?
反应 16.8.3 打字稿 3.3.3
首先,确保您的高阶组件提供了您的组件感兴趣的道具。以className
为例。
interface WithClassName {
className: string;
}
我们的 withClassName
HOC 会接受一个准备好接受 className
作为 prop 的组件,return 一个不再接受 className
的组件。
export function withClassName<T extends React.ComponentType<Partial<WithClassName>>>(Component: T): React.FunctionComponent<Omit<React.ComponentProps<T>, keyof WithClassName>> {
return props => React.createElement(Component, { className: "foo", ...props });
}
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
用法:
const NewSelect = withClassName(Select);
好的 - 所以我已经设法让它工作了。当前的解决方案满足了我的两个关键要求:
推断
defaultProps
被包装的子组件,如果它们存在意味着由 HoC 包装器生成的组件不需要显式传递它们。公开 HoC 包装器属性和底层子组件属性的联合,以便 Intellisense 显示包装组件上的所有可用属性。
在我试图简化实际发生的事情的过程中,withHoC
函数预期的类型是硬编码的(在我的例子中是 react-select
),因此 withHoC
包装器将只接受 react-select
Select
组件进行包装。其他任何东西都可能会引发类型错误。
This link 描述了一些代码,这些代码可能能够自动推断出要被 withHoC
包装的组件类型,从而使 withHoC
可以与 [=19 以外的组件类型一起重用=] Select
。
// node dependencies used (because dependencies mutate so much this may not work in other versions):
// "react": "^16.8.2",
// "react-dom": "^16.8.2",
// "typescript": "^3.3.3",
// "react-select": "2.4.1",
// "@types/react": "^16.8.3",
// "@types/react-dom": "^16.8.2",
// "@types/react-select": "^2.0.13",
// Visual Studio 2017 TypeScript SDK build 3.3.3
import ReactDOM from "react-dom"; // (Optional - just for testing)
import React from "react";
import Select from "react-select";
// Properties shape for React Select (See react-select @type definitions)
import { Props } from "react-select/lib/Select";
// The properties we are want to add so that our resultant wrapped component contains all of its own properties plus the extra properties specified here
interface IHOCProps {
bar: string;
}
function withHoC(WrappedComponent: React.ComponentType<Props>) {
return class SomeHOC extends React.Component<IHOCProps & Props> {
// If 'bar' isn't specified, configure a default (this section is optional)
static defaultProps = {
bar: "default bar"
};
public render(): JSX.Element {
return <><div>{this.props.bar}</div><WrappedComponent {...this.props as any} /></>;
}
};
}
const WrappedSelect = withHoC(Select);
export { WrappedSelect, Select };
// Test it out (Optional). If using Visual Studio 2017 or some other IDE with intellisense,
// <WrappedSelect /> should show all the 'react-select' properties and the HoC property (bar).
// Additionally, all the defaultProps for 'react-select' are automatically inferred so no TypeScript errors about missing props when using <WrappedSelect />.
const TestMe = () =>
<>
<WrappedSelect bar="bumble monkey">
<WrappedSelect />
<Select />
</>;
// Append the result to an HTML document body element
ReactDOM.render(<TestMe />,document.getElementsByTagName("body")[0]);
有些代价高昂的胜利,因为它不能跨类型重复使用,但它确实有效。
最后一个金块;如果您使用 Visual Studio 2017 和 Node for TypeScript,请确保 TypeScript SDK 版本与您的 npm 节点包同步,否则 IDE 会报告在执行命令行时不会自行显示的错误编译(这给我带来了无穷无尽的红鲱鱼问题)。
Microsoft 发布了 this Url,它很少更新并且可能已过时,但公司中没有人注意到。
总是可以在 GitHub here 上找到最新的 SDK,它通常总是与 npm 和 nuget 包一起发布。