TS useRef报错:HTMLDivElement|null无clientHeight等属性优雅解决
报错原因
useRef<HTMLDivElement>(null) 创建的ref对象,current属性的默认类型为 HTMLDivElement | null。
你最初写的可选链scrollRef?.current解构写法,只会在scrollRef本身为null/undefined时短路,但scrollRef本身是RefObject实例永远不会为空,真正可能为空的是scrollRef.current;且可选链取值得到的scrollTop/scrollHeight/clientHeight类型会自动推导为number | undefined,直接参与运算时TS会抛出类型不存在的错误。
你写的提前return判断之所以生效,是因为这是TS可识别的类型收窄逻辑:判断为空直接return后,后续代码块中scrollRef.current的类型会自动排除null,收窄为确定的HTMLDivElement类型,解构属性自然不会报错。
更简洁的空守卫替代写法
你可以根据场景选以下几种写法,都能在不写单独if提前return块的前提下解决类型报错:
- 写法1:临时变量+逻辑与短路判断
const getActivityFeedOnScroll = () => { const el = scrollRef.current; el && el.scrollTop + el.clientHeight === el.scrollHeight && fetchNextPage(); };
先把current存到临时变量里,用逻辑与做短路判断,只有el不为空时才会执行后续的滚动高度判断和数据拉取逻辑,TS可以正确识别这种收窄。
- 写法2:空值合并+解构默认值
const getActivityFeedOnScroll = () => { const { scrollTop = 0, scrollHeight = 0, clientHeight = 0 } = scrollRef.current ?? {}; scrollTop + clientHeight === scrollHeight && fetchNextPage(); };
用??空值合并运算符,当scrollRef.current为null时,fallback到空对象做解构,同时给三个高度属性设置默认值0,保证参与运算的永远是number类型,不需要额外判断空值。
- 写法3:非空断言(仅适用于100%确定ref不为空的场景)
如果你的滚动事件是在组件挂载后才绑定,能保证函数执行时DOM已经渲染完成、ref一定有对应元素,可以直接用非空断言跳过类型检查:
const getActivityFeedOnScroll = () => { const {scrollTop, scrollHeight, clientHeight} = scrollRef.current!; scrollTop + clientHeight === scrollHeight && fetchNextPage(); };
注意:非空断言属于TS类型逃逸语法,不会做运行时校验,如果ref还没绑定元素就触发了滚动函数,会抛出运行时错误,谨慎使用。
内容的提问来源于stack exchange,提问作者Anik Ahmed fathe

