如何修复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
相关产品推荐
相关产品推荐

