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

如何修复TypeScript错误:this expression is not callable.ts(2349)

解决TypeScript中Context setState调用报错:this expression is not callable. Type '{}' has no call signatures.ts(2349)

问题原因

你的UserContext创建时未正确定义类型,TypeScript自动推断的类型存在错误,导致从useContext获取的setUser被识别为可能是{}类型(而非状态更新函数),调用时触发类型校验报错。

具体来说,React.createContext([{}, () => {}])会让TypeScript将这个数组推断为({} | (() => void))[],解构后setUser的类型是{} | (() => void),当你尝试调用setUser(...)时,TypeScript无法确定它是可调用的函数,因此抛出错误。

解决方案

步骤1:定义用户状态的TypeScript接口

先明确用户状态的结构,包含需要的字段:

interface UserState {
  username: string;
  password: string;
  token?: string; // 登录后新增的token字段
}

步骤2:正确创建UserContext并指定类型

修改authContext.tsx,给createContext指定明确的类型,确保它是[UserState, React.Dispatch<React.SetStateAction<UserState>>]:

import React, { PropsWithChildren, useState, createContext } from "react"

interface UserState {
  username: string;
  password: string;
  token?: string;
}

// 明确指定Context类型,初始值匹配UserState结构和空函数
const UserContext = createContext<[UserState, React.Dispatch<React.SetStateAction<UserState>>]>([
  { username: '', password: '' },
  () => {}
])

const initialState: UserState = {
  username: '',
  password: '',
}

const UserProvider = (props: PropsWithChildren<unknown>) => {
  const [state, setState] = useState<UserState>(initialState)

  return (
    <UserContext.Provider value={[state, setState]}>
      {props.children}
    </UserContext.Provider>
  )
}

export { UserContext, UserProvider }

步骤3:优化Login.tsx中的类型(可选但推荐)

去掉any类型,给事件参数和API返回数据指定精确类型:

function Login() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');
  const [showError, setShowError] = useState(false);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [user, setUser] = useContext(UserContext);

  // 给表单事件指定正确类型
  const handleLogin = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    setIsSubmitting(true)
    setError('')

    const genericErrorMessage = 'Something went wrong! Please try again later.'

    fetch('http://localhost:8081/users/login', {
      method: 'POST',
      credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username, password }),
    })
      .then(async response => {
        setIsSubmitting(false)
        if (!response.ok) {
          if (response.status === 400) {
            setError("Please fill all the fields correctly!")
          } else if (response.status === 401) {
            setShowError(true)
            setError("Invalid username and password combination.")
          } else {
            setError(genericErrorMessage)
          }
        } else {
          // 定义API返回数据类型
          interface LoginResponse {
            token: string;
          }
          const data: LoginResponse = await response.json()
          setUser((oldValues) => {
            return { ...oldValues, token: data.token }
          })
          workflow.go(Workflow.Tasks)
        }
      })
      .catch(error => {
        setIsSubmitting(false)
        setError(genericErrorMessage)
      })
  }

  const handleKeyDown: React.KeyboardEventHandler<HTMLInputElement> = ({ key }) => {
    setShowError(false);
    // 用key替代已废弃的keyCode
    if (key === 'Enter') handleLogin(new React.FormEvent<HTMLFormElement>({}));
  };
}

关键说明

  • 给Context指定明确类型是核心解决方法,TypeScript需要确认Context的值是[状态对象, 状态更新函数]的固定结构,而非不确定的联合类型。
  • 避免使用any类型,给变量和参数添加精确类型,能让TypeScript提供有效的类型检查和开发提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 10:36:59