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

如何在TypeScript函数组件中定义Context类型?解决TS2339错误

解决TypeScript中React Context的类型错误:Property 'state'/'dispatch' does not exist on type '{}'

错误原因

你遇到的问题核心是TypeScript对React Context的类型推断逻辑:当你用React.createContext({})创建Context时,TS会自动把这个Context的类型推断为{}(空对象)。所以当你在LanguagePicker或MenuItem中解构state和dispatch时,TS找不到这些属性,自然抛出2339错误。

简单说,TypeScript不知道你的Context里会包含state和dispatch这两个属性,你必须明确告诉它这些属性的类型结构。

解决方案步骤

1. 定义必要的类型/接口

首先,我们需要为应用状态、Action、以及Context的值定义清晰的TypeScript类型,让TS能识别各个部分的结构:

// 定义应用状态的接口
interface AppState {
  lang: string;
  color: string;
}

// 定义Action的类型:涵盖reducer中处理的所有操作
type AppAction = 
  | { type: "change-language"; payload: string }
  | { type: "reset" }; // 把你原来的null分支改成明确的action类型,更符合规范

// 定义Context的值类型:明确包含state和dispatch
interface AppContextValue {
  state: AppState;
  dispatch: React.Dispatch<AppAction | null>; // 兼容reducer接受的null参数
}

2. 创建带正确类型的Context

接下来,创建Context时指定我们定义的AppContextValue类型,并且提供符合该类型的默认值(既避免TS报错,也能防止组件在未被Provider包裹时出现运行时意外):

const AppContext = React.createContext<AppContextValue>({
  state: { lang: 'en', color: 'blue' },
  dispatch: () => {}, // 空的默认dispatch函数
});

3. 修正Reducer中的类型与逻辑问题

给reducer加上类型注解,让TS明确state和action的类型,同时修复reducer里的小bug(比如default分支错误返回{initialState},会导致state结构不符合预期):

const reducer = (state: AppState, action: AppAction | null): AppState => {
  if (action === null ) {
    localStorage.removeItem("state"); // 你原来写的是"action",这里应该对应存储的key"state"
    return initialState;
  }
  switch (action.type) {
    case "change-language":
      return { ...state, lang: action.payload };
    default:
      return state; // 原来的{initialState}会把state变成嵌套结构,不符合AppState类型
  }
};

4. 调整localStorage的类型断言(可选但更严谨)

从localStorage读取数据时,给JSON.parse的结果加上类型断言,让TS明确它的类型:

const localState = JSON.parse(localStorage.getItem("state")!) as AppState | null;

完整修正后的代码

把所有修改整合起来,最终代码如下:

import React, { useContext, useReducer, useEffect } from 'react';

// 定义核心类型
interface AppState {
  lang: string;
  color: string;
}

type AppAction = 
  | { type: "change-language"; payload: string }
  | { type: "reset" };

interface AppContextValue {
  state: AppState;
  dispatch: React.Dispatch<AppAction | null>;
}

// 创建带类型的Context
const AppContext = React.createContext<AppContextValue>({
  state: { lang: 'en', color: 'blue' },
  dispatch: () => {},
});

const App = () => {
  const initialState: AppState = { lang: 'en', color: 'blue'};
  
  // 带类型注解的reducer
  const reducer = (state: AppState, action: AppAction | null): AppState => {
    if (action === null ) {
      localStorage.removeItem("state");
      return initialState;
    }
    switch (action.type) {
      case "change-language":
        return { ...state, lang: action.payload };
      default:
        return state;
    }
  };

  const localState = JSON.parse(localStorage.getItem("state")!) as AppState | null;
  const [state, dispatch] = useReducer(
    reducer, 
    localState || initialState 
  );

  const value = { state, dispatch };

  return (
    <AppContext.Provider value={value}>
      <LanguagePicker />
      <Menu />
    </AppContext.Provider>
  );
}

const Menu = () => <MenuItem />;

const LanguagePicker = () => {
  // 现在TS能正确识别state和dispatch的类型了
  const { state, dispatch } = useContext(AppContext);
  
  const setLanguage = (lang: string) => () => dispatch({ type: "change-language", payload: lang });

  useEffect(() => {
    localStorage.setItem("state", JSON.stringify(state));
  }, [state]);

  return (
    <div>
      <button onClick={setLanguage('en')}>English</button>
      <button onClick={setLanguage('fr')}>French</button>
    </div>
  );
}

const MenuItem = () => {
  const { state } = useContext(AppContext);
  return (
    <div>
      <p>Locale: {state.lang}</p>
    </div>
  );
}

export default App;

额外说明

  • 为什么之前按类组件方式定义无效?因为类组件的Context类型定义逻辑和函数组件一致,核心都是要给Context指定正确的泛型类型,而不是仅单独定义状态接口。
  • 推荐给Context提供默认值:这样即使组件没有被Provider包裹,也不会出现运行时错误,同时TS也能正确推断类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:10:17