使用Next.js在打印脚本中键入重定向摘要 [英] Typing a redirect HOC in TypeScript with Next.js
问题描述
我正在使用TypeScrip构建一个Next.js项目。我正在尝试键入基于Redux状态重定向用户的即席。
以下是我到目前为止的情况:
import { RootState } from 'client/redux/root-reducer';
import Router from 'next/router.js';
import { curry } from 'ramda';
import React, { useEffect } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import hoistStatics from './hoist-statics';
function redirect(predicate: (state: RootState) => boolean, path: string) {
const isExternal = path.startsWith('http');
const mapStateToProps = (state: RootState) => ({
shouldRedirect: predicate(state),
});
const connector = connect(mapStateToProps);
return hoistStatics(function <T>(Component: React.ComponentType<T>) {
function Redirect({
shouldRedirect,
...props
}: T & ConnectedProps<typeof connector>): JSX.Element {
useEffect(() => {
if (shouldRedirect) {
if (isExternal && window) {
window.location.assign(path);
} else {
Router.push(path);
}
}
}, [shouldRedirect]);
return <Component {...props} />;
}
return Redirect;
});
}
export default curry(redirect);
我觉得我离得很近了,但我不太明白最后一个错误。<Component {...props} />
喊声:
Type 'Pick<T & { shouldRedirect: boolean; } & DispatchProp<AnyAction>, "dispatch" | Exclude<keyof T, "shouldRedirect">>' is not assignable to type 'IntrinsicAttributes & T & { children?: ReactNode; }'.
Type 'Pick<T & { shouldRedirect: boolean; } & DispatchProp<AnyAction>, "dispatch" | Exclude<keyof T, "shouldRedirect">>' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'Pick<T & { shouldRedirect: boolean; } & DispatchProp<AnyAction>, "dispatch" | Exclude<keyof T, "shouldRedirect">>'.
这是怎么回事?我可以通过如下方式键入内部hirc来传递代码:
return hoistStatics(function <T>(Component: React.ComponentType<T>) {
function Redirect(
props: T & ConnectedProps<typeof connector>,
): JSX.Element {
useEffect(() => {
if (props.shouldRedirect) {
if (isExternal && window) {
window.location.assign(path);
} else {
Router.push(path);
}
}
}, [props.shouldRedirect]);
return <Component {...props} />;
}
return Redirect;
});
但这将意味着Component
获得了一个名为shouldRedirect
的道具。它应该只接收和传递它通常收到的道具。
我不得不禁用两个警告。
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return hoistStatics(function <T>(Component: React.ComponentType<T>) {
和
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return connector(Redirect);
hoistStatics
是一种更高级别的点对点,如下所示。
import hoistNonReactStatics from 'hoist-non-react-statics';
const hoistStatics = (
higherOrderComponent: <T>(
Component: React.ComponentType<T>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) => (props: T & any) => JSX.Element,
) => (BaseComponent: React.ComponentType) => {
const NewComponent = higherOrderComponent(BaseComponent);
hoistNonReactStatics(NewComponent, BaseComponent);
return NewComponent;
};
export default hoistStatics;
其类型也一起被黑客攻击(如您在禁用警告中看到的)。
如何键入这两个函数?
编辑:
在Linda的帮助下hoistStatics
现在起作用了。但是,Redirect
仍然会产生问题。I asked a new question for this here。
function redirect(predicate: (state: RootState) => boolean, path: string) {
const isExternal = path.startsWith('http');
const mapStateToProps = (state: RootState) => ({
shouldRedirect: predicate(state),
});
const connector = connect(mapStateToProps);
return hoistStatics(function <T>(
Component: React.ComponentType<Omit<T, 'shouldRedirect'>>,
) {
function Redirect({
shouldRedirect,
...props
}: T & ConnectedProps<typeof connector>): JSX.Element {
useEffect(() => {
if (shouldRedirect) {
if (isExternal && window) {
window.location.assign(path);
} else {
Router.push(path);
}
}
}, [shouldRedirect]);
return <Component {...props} />;
}
return connector(Redirect);
});
}
connector(Redirect)
的行仍然抛出错误:
Argument of type '({ shouldRedirect, ...props }: T & { shouldRedirect: boolean; } & DispatchProp<AnyAction>) => Element' is not assignable to parameter of type 'ComponentType<Matching<{ shouldRedirect: boolean; } & DispatchProp<AnyAction>, T & { shouldRedirect: boolean; } & DispatchProp<AnyAction>>>'.
Type '({ shouldRedirect, ...props }: T & { shouldRedirect: boolean; } & DispatchProp<AnyAction>) => Element' is not assignable to type 'FunctionComponent<Matching<{ shouldRedirect: boolean; } & DispatchProp<AnyAction>, T & { shouldRedirect: boolean; } & DispatchProp<AnyAction>>>'.
Types of parameters '__0' and 'props' are incompatible.
Type 'PropsWithChildren<Matching<{ shouldRedirect: boolean; } & DispatchProp<AnyAction>, T & { shouldRedirect: boolean; } & DispatchProp<AnyAction>>>' is not assignable to type 'T & { shouldRedirect: boolean; } & DispatchProp<AnyAction>'.
Type 'PropsWithChildren<Matching<{ shouldRedirect: boolean; } & DispatchProp<AnyAction>, T & { shouldRedirect: boolean; } & DispatchProp<AnyAction>>>' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'PropsWithChildren<Matching<{ shouldRedirect: boolean; } & DispatchProp<AnyAction>, T & { shouldRedirect: boolean; } & DispatchProp<AnyAction>>>'.
推荐答案
错误
在拼接各种展开操作的道具时,TypeScrip可能会出错,因为边缘情况会导致无效对象。
return hoistStatics(function <T>(Component: React.ComponentType<T>) {
function Redirect({
shouldRedirect,
...props
}: T & ConnectedProps<typeof connector>): JSX.Element {
/*...*/
return <Component {...props} />;
}
组件道具T
可以是任何对象。如果T
包括必需的属性shouldRedirect
,则会破坏这一点。
{shouldRedirect, ...props}
时,我们将shouldRedirect
从道具上解体。因此,要知道这个props
变量不可能包含变量shouldRedirect
。它是Omit<T, 'shouldRedirect'>
类型。如果我们处于T
具有必需shouldRedirect
道具的边缘情况下,则这里有一个问题:因为props
不能满足T
的要求。您得到的错误很难读懂,因为它输出的类型非常冗长,但该类型表示props
对象。可以用任意类型实例化‘T’,这基本上意味着这些道具可能满足T
,也可能不满足T
,具体取决于T
是什么。
解决方案
在编写HOC时经常会出现这个问题,如果需要的话,您总是可以求助于{...props as T}
。我所做的通常是传递整个对象,就像您在代码块中所做的那样,我可以让代码传递。如果我们想删除不需要的属性并保持它的类型安全,我们需要禁止坏的边缘情况。我们对T
保持模糊,但声明我们的Component
不能通过使用Omit
来要求此道具。
function <T>(Component: React.ComponentType<Omit<T, 'shouldRedirect'>>)
键入hoistStatics
hoistStatics
是一个函数,该函数接受hoct并返回hocc。我们知道,HOC可以操纵道具,使得组合组件(Outer
)的道具与基础组件(Inner
)的道具不同。所以我们可以在这里使用两个泛型。无论这两种类型是什么,hoistStatics
都不应更改它们,而应返回具有与其参数相同的签名的HOC。
我们定义了一个泛化的HOC
签名来保持事物的整洁
type HOC<Inner, Outer = Inner> = (
Component: React.ComponentType<Inner>,
) => React.ComponentType<Outer>
然后我们使用该类型来描述hoistStatics
const hoistStatics = <I, O>(
higherOrderComponent: HOC<I, O>
): HOC<I, O> => (BaseComponent: React.ComponentType<I>) => {
const NewComponent = higherOrderComponent(BaseComponent);
hoistNonReactStatics(NewComponent, BaseComponent);
return NewComponent;
};
这篇关于使用Next.js在打印脚本中键入重定向摘要的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!