ts中对高阶组件进行form注入报错

react@16.14.0 typescript@4.1.5

import React from "react";
import { Form, FormInstance } from "antd";

export interface InjectedWithFormProps {
  form: FormInstance<any>;
}

export default function withForm<P extends InjectedWithFormProps>(WrappedComponent: React.ComponentType<P>) {
  return function WithForm(props: Subtract<P, InjectedWithFormProps>) {
    const [form] = Form.useForm();

    return <WrappedComponent form={form} {...props}></WrappedComponent>;
  };
}

Subtract 声明

declare type SetDifference<A, B> = A extends B ? never : A;

declare type SetComplement<A, A1 extends A> = SetDifference<A, A1>;

declare type Subtract<T extends T1, T1 extends object> = Pick<
  T,
  SetComplement<keyof T, keyof T1>
>;

上述代码是用antd v4的form进行注入,用来解决class form ref 在第一次render阶段拿不到form实例的问题,采用的是v4 版本 form 在 class 模式下,无法立即获得 formRef.current,注入form的类型写法参考了【译】TypeScript中的React高阶组件,但是对组件进行form注入约束的时候会报错
image.png

查了相关文档也没找到好的解决方案,不知道是不是typescript的bug
Invalid "could be instantiated with a different subtype of constraint" error
How to fix TS2322: “could be instantiated with a different subtype of constraint 'object'”?

阅读 3.4k
1 个回答

你的问题可以简化为:

type Foo = {
    a: number;
    b: string;
}
function test<T extends Foo>(v: Omit<T, 'a'>): T {
    const ret = {
        ...v,
        a: 1
    };
    return ret; // Type 'Omit<T, "a"> & { a: number; }' is not assignable to type 'T'.
}

本质来说,就是因为你忽略 T 类型中的一个 key,然后再用 & 操作补充这个 key,TS 只认为这个类型是 T 的限制类型即 Foo 的合法子类型,但是和 T 类型本身并不是完全等价的,而实际 T 可能是 Foo 类型的任意子类型,可能比 'Omit<T, "a"> & { a: number; }' 包含更多的细节,因此类型不匹配。

试想如果我这样调用你的函数:

test<{a: 5, b: string}>({b: 'lalala'});

T 实际为 {a:5, b: string} 是 Foo 的子类型,而你的返回值显然不符合 T 类型,这就矛盾了,这说明你的 test 函数类型设计存在漏洞,TS 自然就把这个错误报出来让你修正了。

所以,现阶段要解决这个问题,就只能 as 大法了,你说它是这个类型那就是,你自己 js 层面保证没问题, TS 就不管你了。

export interface InjectedWithFormProps {
  form: FormInstance<any>;
}

export default function withForm<P extends InjectedWithFormProps>(WrappedComponent: React.ComponentType<P>) {
  return function WithForm(props: Omit<P, 'form'>) {
    const [form] = Form.useForm();
    const combinedProps = {
        form,
        ...props
    } as P;
    return <WrappedComponent {...combinedProps}></WrappedComponent>;
  };
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题