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

React自定义Hook返回数组时出现"This expression is not callable"错误的技术问询

解决React自定义Hook数组解构的TypeScript类型报错问题

这个问题的核心是TypeScript对数组返回值的类型推断逻辑:默认情况下,它会把你返回的[body, setProp, setBody]推断成一个联合类型数组(每个元素的类型是jsonType | 函数 | 函数的混合),而不是你期望的固定顺序的元组。这就导致TypeScript无法确定你解构出来的第二个、第三个元素到底是可调用的函数还是普通对象,所以会抛出"This expression is not callable"的错误。

两种可行的解决方案

方案1:显式指定Hook的返回类型为元组

通过定义明确的元组类型,直接告诉TypeScript返回数组中每个位置的具体类型:

import { useState, Dispatch, SetStateAction } from 'react';

// 基础JSON类型定义
type JsonType = { [property: string]: any };
// 定义Hook的返回元组类型:[当前状态, 设置单个属性的函数, 整体设置状态的函数]
type UseJSONStateReturn<T extends JsonType> = [
  T,
  (property: keyof T, value: T[keyof T]) => void,
  Dispatch<SetStateAction<T>>
];

const useJSONState = <T extends JsonType>(json: T): UseJSONStateReturn<T> => {
  const [body, setBody] = useState<T>(json);
  
  // 优化setProp:使用函数式更新,避免闭包导致的旧值问题
  const setProp = (property: keyof T, value: T[keyof T]) => {
    setBody(prevBody => ({ ...prevBody, [property]: value }));
  };

  return [body, setProp, setBody];
};

export default useJSONState;

这里还做了两个实用优化:

  • 用泛型T代替固定的JsonType,让Hook支持特定结构的JSON,类型检查更严格
  • 使用函数式更新setBody(prevBody => ...),避免依赖外部body变量导致的状态更新异常

方案2:使用as const断言简化类型推断

如果你不想额外定义返回类型,可以用as const断言让TypeScript把返回数组推断为只读元组,固定每个位置的类型:

import { useState } from 'react';

type JsonType = { [property: string]: any };

const useJSONState = <T extends JsonType>(json: T) => {
  const [body, setBody] = useState<T>(json);
  
  const setProp = (property: keyof T, value: T[keyof T]) => {
    setBody(prevBody => ({ ...prevBody, [property]: value }));
  };

  // 用as const断言强制TypeScript推断为固定顺序的元组
  return [body, setProp, setBody] as const;
};

export default useJSONState;

使用效果

现在你就可以像useState一样自由解构,而且TypeScript会正确识别每个函数的类型:

// 示例调用
const [userInfo, setUserProp, setUserInfo] = useJSONState({ name: "Alice", age: 30 });

// 类型检查生效:只能传入存在的属性,值的类型也会自动匹配
setUserProp("age", 31); // ✅ 正确
setUserProp("name", "Bob"); // ✅ 正确
setUserProp("invalidKey", "test"); // ❌ TypeScript报错:invalidKey不是userInfo的属性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 15:18:12