使用Next.js在打印脚本中键入重定向摘要 [英] Typing a redirect HOC in TypeScript with Next.js

查看:11
本文介绍了使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆