告诉 TypeScript 应该允许使用任意类型实例化 T [英] Telling TypeScript that T should be allowed to be instantiated with an arbitrary type

查看:179
本文介绍了告诉 TypeScript 应该允许使用任意类型实例化 T的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这里是最小可重现的链接 我必须包含外部库 Redux 和 React Router Dom 才能忠实地重现它.

Here is a link to a minimal reproducible example. I had to include the external libraries Redux and React Router Dom to reproduce it faithfully.

我正在使用 Redux 和 TypeScript 构建 Next.js 应用.

I'm building a Next.js app with Redux and TypeScript.

我有一个高阶组件来提升静力学.

I have a higher-order higher-order component that hoists statics.

import hoistNonReactStatics from 'hoist-non-react-statics';

type HOC<Inner, Outer = Inner> = (
  Component: React.ComponentType<Inner>,
) => React.ComponentType<Outer>;

const hoistStatics = <I, O>(higherOrderComponent: HOC<I, O>): HOC<I, O> => (
  BaseComponent: React.ComponentType<I>,
) => {
  const NewComponent = higherOrderComponent(BaseComponent);
  hoistNonReactStatics(NewComponent, BaseComponent);
  return NewComponent;
};

export default hoistStatics;

利用这个,我想写一个 redirect HOC 可以根据 Redux 状态重定向用户.这是我所拥有的.

Using this, I want to write a redirect HOC that can redirect users based on Redux state. Here is what I have.

import Router from 'next/router.js';
import { curry } from 'ramda';
import { useEffect } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { RootState } from 'redux/root-reducer';

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<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);
  });
}

export default curry(redirect);

这里的问题是 return connector(Redirect); 抛出以下 TS 错误.

The problem here is that return connector(Redirect); throws the following TS error.

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>>>'.ts(2345)

我该如何解决这种打字问题?我认为将任何道具"告诉 TypeScriptThoist-statics 中,然后在 Redirect 的 props 中就足够了.

How can I fix this typing? I thought that telling TypeScript about the "any props" T in hoist-statics and then in Redirect's props would suffice.

推荐答案

所以有一个实际的错误,然后有一堆废话.

So there's an actual mistake and then there's a bunch of bullshit.

实际错误是Redirect需要使用PropsWithChildren.

废话与Matching 来自 react-redux 包的实用程序类型,用于将注入的 props 与组件自己的 props 连接起来.

The bullshit has to do with the Matching utility type from the react-redux package which is used for joining the injected props with the components own props.

/**
 * A property P will be present if:
 * - it is present in DecorationTargetProps
 *
 * Its value will be dependent on the following conditions
 * - if property P is present in InjectedProps and its definition extends the definition
 *   in DecorationTargetProps, then its definition will be that of DecorationTargetProps[P]
 * - if property P is not present in InjectedProps then its definition will be that of
 *   DecorationTargetProps[P]
 * - if property P is present in InjectedProps but does not extend the
 *   DecorationTargetProps[P] definition, its definition will be that of InjectedProps[P]
 */
export type Matching<InjectedProps, DecorationTargetProps> = {
    [P in keyof DecorationTargetProps]: P extends keyof InjectedProps
        ? InjectedProps[P] extends DecorationTargetProps[P]
            ? DecorationTargetProps[P]
            : InjectedProps[P]
        : DecorationTargetProps[P];
};

我们在这里得到了这两种类型之间的错误,说A不可分配给B.

We get an error between these two types here, saying that A<T> is not assignable to B<T>.

type A<T> = PropsWithChildren<Matching<{ shouldRedirect: boolean; } & DispatchProp<AnyAction>, T & { shouldRedirect: boolean; } & DispatchProp<AnyAction>>>
type B<T> = PropsWithChildren<T & { shouldRedirect: boolean; } & DispatchProp<AnyAction>>

这两种类型在 99.9% 的情况下基本相同.再次,我们正在研究什么奇怪的边缘情况会使它成为真的.

Those two types are basically the same 99.9% of the time. Again we are looking at what weird edge case would make it not true.

基本上唯一一次它不起作用是如果 T 有一个 prop shouldRedirectdispatch 的类型比什么更窄我们注入,即.true 而不是 boolean.

Basically the only time it doesn't work is if T has a prop shouldRedirect or dispatch with a type that is narrower than what we injected, ie. true instead of boolean.

InjectedProps{ shouldRedirect: boolean;&DispatchProp

DecorationTargetPropsT &{ 应该重定向:布尔值;&DispatchProp>

老实说,我的大脑在思考哪个项目符号规则有问题,但我可以看到比较 这个游乐场.

Honestly my brain is exploding thinking about which of the bullet point rules is the problem, but I can see the issue comparing lines 87 and 88 of this playground.

所以在这一点上......只是任何并继续你的生活.

So at this point... just as any and move on with your life.

这篇关于告诉 TypeScript 应该允许使用任意类型实例化 T的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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