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注入约束的时候会报错
查了相关文档也没找到好的解决方案,不知道是不是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'”?
你的问题可以简化为:
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>;
};
}