类型安全高阶React组件和无状态功能组件
问题描述:
我试图编写更高阶的组件来检查用户是否已通过身份验证。我使用15.5.4做出反应,并@类型/反应15.0.21,和我(简化)代码如下:类型安全高阶React组件和无状态功能组件
import * as React from 'react';
interface IAuthProps {
authenticated: boolean
}
function authenticated1<Props extends Object>(wrapped: React.ComponentClass<Props> | React.SFC<Props>):
React.SFC<Props & IAuthProps> {
return (props: Props & IAuthProps): React.ReactElement<any> => {
if (props.authenticated) {
return React.createElement(wrapped, props);
} else {
return <h1>Unauthorized!</h1>
}
}
}
然而,在编译调用失败createElement
:
TS2345:Argument of type 'ComponentClass<Props> | StatelessComponent<Props>' is not assignable to parameter of type 'ComponentClass<Props>'.
Type 'StatelessComponent<Props>' is not assignable to type 'ComponentClass<Props>'.
Type 'StatelessComponent<Props>' provides no match for the signature 'new (props?: Props | undefined, context?: any): Component<Props, ComponentState>'
由于@types/react
将React.createElement
声明为超载函数,并且Typescript can't resolve overloads with union types错误并不让人惊讶。
然而@types/react
提供了在工会每种合格的过载(SFCEelement
继承ReactElement
所以返回类型兼容):
function createElement<P>(
type: ComponentClass<P>,
props?: Attributes & P,
...children: ReactNode[]): ReactElement<P>;
function createElement<P>(
type: SFC<P>,
props?: Attributes & P,
...children: ReactNode[]): SFCElement<P>;
为了使代码编译我只是需要强制打字稿考虑要么为联合类型的相应分支重载,但我不知道如何做到这一点。
如何区分ComponentClass
和SFC
以使Typescript为类型检查选择相应的过载?
PS:目前我只是迫使它通过传递wrapped as React.ComponentClass<Props>
这是安全的挑头超载,因为超载都叫出它接受两个参数类型相同的运行时功能,但我宁愿不喜欢在这里“作弊”,而是让系统保证安全。
答
我认为目前不可能这样做,因为它需要改变类型定义。
要使用Type Guard功能可以缩小对象的类型,我们需要扩展一些定义。
interface StatelessComponent<P> {
(props: P & { children?: ReactNode }, context?: any): ReactElement<any>;
type: 'StatelessComponent'; // NEW ONE
propTypes?: ValidationMap<P>;
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
interface ComponentClass<P> {
new (props?: P, context?: any): Component<P, ComponentState>;
type: 'ComponentClass'; // NEW ONE
propTypes?: ValidationMap<P>;
contextTypes?: ValidationMap<any>;
childContextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
然后我们将能够做这样的事情
function createElementWrapper<Props extends Object>(wrapped: React.ComponentClass<Props> | React.SFC<Props>, props: any) {
if (wrapped.type === "StatelessComponent") {
return React.createElement(wrapped, props); // wrapped is a StatelessComponent
} else {
return React.createElement(wrapped, props); // wrapped is a ComponentClass
}
}
这可能是一个好主意,以创建关于在definitelytyped库这一问题的问题