通过HOC传递组件会导致defaultProps信息丢失给打字稿编译器。例如
themed.tsx
export interface ThemedProps {
theme: {};
}
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
export type Subtract<T extends K, K> = Omit<T, keyof K>;
const themed = <P extends ThemedProps = ThemedProps>(
ComponentToWrap: React.ComponentType<P>
) => {
return class ThemeWrappedComponent extends React.Component<
Subtract<P, ThemedProps>
> {
static displayName = `themed(${ComponentToWrap.displayName})`;
theme = () => {
return {}
};
render() {
return (
<ComponentToWrap
{...this.props as P}
theme={this.theme()}
/>
);
}
}
};
Foo.tsx
interface FooProps {
theme: object,
name: string,
}
class Foo extends React.Component<FooProps> {
static defaultProps = {
name: 'world'
}
render() {
return <span>hello ${this.props.name}</span>
}
}
export default themed(Foo);
实例化<Foo />
时,出现编译器错误,提示Property 'name' is missing in type '{}' but required in type 'Readonly<Pick<FooProps, "name">>'.
。
我知道有一种方法可以使用JSX.LibraryManagedAttributes
来解决这种问题,但是我不知道如何,而且我也找不到关于该功能的任何文档。
答案 0 :(得分:1)
您必须利用JSX.LibraryManagedAttributes
才能从HOC中的包装组件中提取必需的和可选的(默认)道具。这看起来有些棘手:
import React from 'react';
interface FooProps {
theme: string;
name: string;
}
class Foo extends React.Component<FooProps> {
static defaultProps = {
name: 'world',
};
render() {
return <span>hello ${this.props.name}</span>;
}
}
interface ThemedProps {
theme: string;
}
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type Subtract<T extends K, K> = Omit<T, keyof K>;
const themed = <
C extends React.ComponentType<React.ComponentProps<C> & ThemedProps>,
// that's where all the magic happens
ResolvedProps = JSX.LibraryManagedAttributes<C, Subtract<React.ComponentProps<C>, ThemedProps>>
>(
Component: C
) => {
return class ThemeWrappedComponent extends React.Component<ResolvedProps> {
static displayName = `themed(${ComponentToWrap.displayName})`;
render() {
return (
<Component
// proper typecast since ts has fixed type infering on object rest
{...this.props as JSX.LibraryManagedAttributes<C, React.ComponentProps<C>>}
theme="theme"
/>
);
}
};
};
const Wrapped = themed(Foo);
const el = <Wrapped />; // works