react 组件的 ref 到底是什么类型?

2021-09-06 19:35:51 +08:00
 Leviathann

版本是 15,所以没法用 React.createRef 所以写了 callback ref,但是一旦想抽成组件内部函数怎么写都写不对

class A extends Component<{}, {}> {

  createRef = (element: HTMLDivElement | null) => this.rootElement = element
 
  render() {
    return (
      <B ref={this.createRef}/>
    )
  }
}

type Bprops = {
  ref: React.Ref<HTMLDivElement>
}

class B extends Component<Bprops> {
  render() {
    const {ref} = this.props
    return <div ref={ref}/>
  }
}

然后就报错

TS2322: Type '(element: HTMLDivElement | null) => HTMLDivElement | null' is not assignable to type '(Ref<PopUpAd> | undefined) & Ref<HTMLDivElement>'. Type '(element: HTMLDivElement | null) => HTMLDivElement | null' is not assignable to type 'string & ((instance: HTMLDivElement | null) => any)'. Type '(element: HTMLDivElement | null) => HTMLDivElement | null' is not assignable to type 'string'.

完全不知道报错的这些东西哪来的,react 的定义里就一句

type Ref<T> = string | { bivarianceHack(instance: T | null): any }["bivarianceHack"];

而且定义里的 string 是或,而报错里的是交

想去掉报错只能把 createRef 的参数的类型声明去掉,实际上就相当于是 any 把

1851 次点击
所在节点    React
4 条回复
2i2Re2PLMaDnghL
2021-09-07 10:23:50 +08:00
我先解释下报错里的交,为写得段我就把 PopUpAd 和 HTMLDivElement 写成 A 和 B 了

(Ref<A> | undefined) & Ref<B>
= (Ref<A> & Ref<B>) | (undefined & Ref<B>) // 分配律
= Ref<A> & Ref<B> // undefined 交非 undefined 任何为空
= (string | (A|null) => any) & (string | (B|null) => any) // Ref<T> 展开
= (string & string) | (((A|null) => any) & string) | (string & ((B|null) => any)) | (((A|null) => any) & ((B|null) => any)) // 分配律
2i2Re2PLMaDnghL
2021-09-07 10:30:42 +08:00
你这边 (Ref<PopUpAd> | undefined) & Ref<HTMLDivElement> 在不考虑二者有包含关系的情况下就只能是 string 了。

再考虑到 Ref<T> 对 T 逆变,也可以是 Ref<PopUpAd | HTMLDivElement> ?这个部分我验算算不清。
也就是说你的 createRef 应当符合 (PopUpAd | HTMLDivElement | null) => any
至于 PopUpAd 是哪来的,我就没有头绪了
Leviathann
2021-09-07 12:00:34 +08:00
@2i2Re2PLMaDnghL 额,忘了改报错信息
PopUpAd 就是这里的 B 组件
也就是说 A 组件想要持有一个 B 组件中某个 HTMLElement 的 ref
那 A 组件的持有的 ref 就要定义为 HTMLElement | B | null 吗
2i2Re2PLMaDnghL
2021-09-07 20:40:46 +08:00
或者公共父类。看上去 Component 就可以了?

猜想,
因为你的 createRef 传送过程中先后通过了 B.ref 和 Div.ref ,所以必须都符合。
```<B ref={this.createRef}/>``` 这一段可能检查引擎蕴含了 createRef 必须是 Ref<B>,或者说 Component 的定义里包含了这一限定,你的 Bprops 实际是与之相交而非覆盖。
你尝试下把这个 ref 的名字改成 myref 试试?
当然理论上也可以让 B 同时 extends Div,但引入的问题可能比解决的多。

然后发现其实是双变的,bivarianceHack 这名字看上去就是为了让 TS 允许双变,不清楚如何办到的。范畴论推理不是我的强项。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/800228

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX