class 类型
React.Ref
class 直接作为泛型参数似乎会被处理为InstanceType,而 typeof XXclass,反而能真正获取 class的类型。
HOC 后的 ref 类型
场景
HOC 包裹后的组件,不可直接ref获取,因为此时获取的是包裹用组件,入需获取内部组件,通用且兼容的方式就是采取 易名props 来承载 ref,再在包裹用组件内将其通过 ref={易名props} 的方式将其放置在真正需要取的组件的 ref 上。
但因为 react 组件嵌套的类型自推导在当前的 typescript 中无法实现,所以只能曲线救国,过程类似下面的各种断言操作。
HOC之所以可以 compose 是因为接受了环境参数后的 (C: Component<..>)=> Component<..>,确实是进入及返回都是同一样’东西’,只是 props 变得不一样了,所以会无法通过当前的 ts 检查。
组件类型:
compose函数组装 HOC,并自制返回组件的类型,这里需要小心,瞎写的类型会给外部带来很大的误导:
这里实际承载 ref 的是props: wrappedComponentRef,例子来自 antd3.x Form.create
const WrappedComponent = compose<
React.ComponentClass<
PropsNeeded & {
// wrappedComponentRef: React.ClassAttributes<Test>['ref'];
wrappedComponentRef: React.Ref<Test>;
}
>
>(
formWrapper,
connector,
)(Test);
React.Ref
提取泛型及提取过程
这里的过程也是模仿的 React.ElementRef 从 ref 中提取 Instance 的过程,无非就是 原本 react 从组件 class 的 ‘ref’ 属性上 infer,而现在需要从易名 Props 上infer了:
type PropsRef<T> = NonNullable<T> extends React.Ref<infer Instance>
? Instance
: never;
const componentRef = React.useRef<
PropsRef<
React.ComponentPropsWithoutRef<
typeof WrappedComponent
>['wrappedComponentRef']
>
>(null!);
以及使用泛型完整推导:
type PropsForwardElementRef<
C extends
| React.ForwardRefExoticComponent<any>
| { new (props: any): React.Component<any> }
| ((props: any, context?: any) => React.ReactElement | null)
| keyof JSX.IntrinsicElements,
T
> = T extends keyof React.ComponentPropsWithoutRef<C>
? NonNullable<React.ComponentPropsWithoutRef<C>[T]> extends React.Ref<
infer Instance
>
? Instance
: never
: never;
// 从 WrappedComponent 组件中 取到从 wrappedComponentRef 这个 props 挂载的 ref
const componentRef = React.useRef<
PropsForwardElementRef<typeof WrappedComponent, 'wrappedComponentRef'>
>(null!);
与其实现自推导,还不如实现更加合适的断言。
一些解决方案:
https://stackoverflow.com/questions/54355096/how-to-use-hoc-with-redux-compose-and-typescript
https://rjzaworski.com/2017/09/typescript-react-compose
https://dev.to/ascorbic/creating-a-typed-compose-function-in-typescript-3-351i