You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript报错:settings为何被推断为number | MutableRefObject类型?

为什么TypeScript会将settings推断为number | MutableRefObject类型?

我遇到如下TypeScript错误:

Property 'current' does not exist on type 'number | MutableRefObject'

该错误出现在settings ref上。我从未将settings设为number类型,也未指定该类型,不理解为何TypeScript会推断它可能是number,它应该仅为ref类型。相关代码如下:

const useDatGui = (controls) => {
  const [updates, setUpdates] = useState(0)
  const loaded = useRef(false)
  const settings = useRef({
    mode: 'scale',
    roomWidth: defaultRoom.width,
    roomDepth: defaultRoom.depth,
    roomHeight: defaultRoom.height,
    boxWidth: 0.4,
    boxDepth: 0.3,
    boxHeight: 0.2,
  })
  useEffect(() => {
    if (typeof window != 'undefined' && !loaded.current) {
      const gui = new GUI()
      controls.forEach(([setting, { type, options }]) => {
        gui.add(settings.current, setting, options).onChange(() => {
          console.log('changed', setting)
          setUpdates(upd => (upd + 1) % 2)
        })
      })
      loaded.current = true
      return () => {
        gui.destroy()
        loaded.current = false
      }
    }
  }, [])
  return [updates, settings]
}
export default function Page() {
  const { selected, setState } = useContext(MyContext);
  const [updates, settings] = useDatGui([
    ["mode", { type: "select", options: ["scale", "rotate", "translate"] }],
    ["roomHeight", { type: "range", options: 1 }],
    ["roomWidth", { type: "range", options: 1 }],
    ["roomDepth", { type: "range", options: 1 }],
    ["boxDepth", { type: "range", options: 0.1 }],
    ["boxWidth", { type: "range", options: 0.1 }],
    ["boxHeight", { type: "range", options: 0.1 }]
  ])
  const wall = useMemo(() => {
    if (settings.current) {
      return ({
        ...defaultRoom,
        height: settings.current.roomHeight,
        width: settings.current.roomWidth,
        depth: settings.current.roomDepth,
      })
    }
  }, [defaultRoom, settings])
  const box = {
    width: settings.current.boxWidth,
    depth: settings.current.boxDepth,
    height: settings.current.boxHeight
  }
  //...
  return ...

错误原因

问题出在useDatGui的返回值类型推断逻辑上:

  • TypeScript默认会把函数返回的数组推断为元素类型是联合类型的数组,而非固定顺序的元组。
  • 你的useDatGui返回[updates, settings],其中updates是number类型,settings是MutableRefObject类型。TypeScript会将这个返回值推断为(number | MutableRefObject<...>)[],而非严格的[number, MutableRefObject<...>]。
  • 当你在Page组件中解构这个返回值时,TypeScript会认为updates和settings都可能是number或MutableRefObject类型,因此会抛出settings可能是number的错误。

解决方法

有两种简单的方式修复这个类型推断问题:

  1. 用as const断言返回值类型
    在useDatGui的return语句中添加断言,让TypeScript将返回值识别为只读元组:
return [updates, settings] as const;
  1. 显式指定函数返回值的元组类型
    给useDatGui函数添加明确的返回类型定义,同时也可以给controls参数补充类型,让代码更严谨:
// 先定义settings的类型
type Settings = {
  mode: string;
  roomWidth: number;
  roomDepth: number;
  roomHeight: number;
  boxWidth: number;
  boxDepth: number;
  boxHeight: number;
};

// 定义controls的类型
type ControlItem = [string, { type: string; options: string[] | number }];

const useDatGui = (controls: ControlItem[]): [number, React.MutableRefObject<Settings>] => {
  // 原函数内容不变
};

内容的提问来源于stack exchange,提问作者George

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 10:08:10