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

React泛型Context优化:从最近Provider自动推断类型

解决React Context泛型自动推断问题

核心思路

通过调整Context的基础定义和HOC的类型约束,让TypeScript能自动从最近的Provider推断useDataEditingContext的泛型类型,同时保留类型不匹配时的报错能力,且无需使用工厂函数创建多个Context。

具体实现步骤

1. 重新定义Context结构

先创建一个基础Context,将其值的类型设为unknown,后续通过HOC注入具体的泛型类型:

import React from 'react';

// 定义Context的核心操作/状态结构
type DataEditingContextValue<T> = {
  data: T;
  setData: (partialData: Partial<T>) => void;
  // 可添加其他你需要的方法或状态,比如resetData、isEditing等
};

// 创建基础Context,初始值设为undefined,类型暂定为unknown
const DataEditingContext = React.createContext<DataEditingContextValue<unknown> | undefined>(undefined);

2. 优化HOC的类型约束

让HOC捕获传入的初始数据类型,并确保Provider传递的value类型与该类型绑定:

function withDataEditingContext<T>(Component: React.ComponentType) {
  return function ProviderWrapper(props: { initialData: T }) {
    const [data, setData] = React.useState<T>(props.initialData);

    // 组装符合Context结构的value,类型自动匹配T
    const contextValue: DataEditingContextValue<T> = {
      data,
      setData: (partialData) => setData(prev => ({ ...prev, ...partialData })),
      // 这里实现你需要的其他方法,比如resetData: () => setData(props.initialData)
    };

    return (
      <DataEditingContext.Provider value={contextValue}>
        <Component {...props} />
      </DataEditingContext.Provider>
    );
  };
}

3. 改进useDataEditingContext Hook

让Hook自动推断类型,同时在未找到Provider时抛出错误,且手动传入不匹配类型时触发TypeScript报错:

function useDataEditingContext<T = unknown>() {
  const context = React.useContext(DataEditingContext);
  
  if (!context) {
    throw new Error('useDataEditingContext必须在DataEditingContextProvider内部使用');
  }

  // 类型断言结合泛型约束:如果手动传入的T与实际Provider的类型不匹配,TypeScript会报错
  return context as DataEditingContextValue<T>;
}

使用示例

包裹根组件

// 定义你的数据类型
type User = { id: number; name: string; email: string };

// 用HOC包裹根组件
const AppWithData = withDataEditingContext(App);

// 使用时传入初始数据,TypeScript自动推断T为User
ReactDOM.createRoot(document.getElementById('root')!).render(
  <AppWithData initialData={{ id: 1, name: 'Joe', email: 'joe@example.com' }} />
);

子组件中使用Hook

无需手动传入泛型,TypeScript自动从Provider推断类型:

function UserEditForm() {
  const { data, setData } = useDataEditingContext();
  
  // data自动为User类型,setData只能接受Partial<User>的参数
  return (
    <div>
      <input
        value={data.name}
        onChange={(e) => setData({ name: e.target.value })}
      />
      <input
        value={data.email}
        onChange={(e) => setData({ email: e.target.value })}
      />
    </div>
  );
}

类型不匹配时报错

如果手动传入错误的泛型,TypeScript会直接提示类型不兼容:

// ❌ 错误:类型不匹配,实际Provider提供的是User类型
const { data } = useDataEditingContext<Product>();

方案优势

  • 避免了工厂函数创建多个Context的额外开销,只用一个基础Context即可。
  • 无需重复传入泛型类型,TypeScript自动完成上下文推断。
  • 保留了类型安全:手动传入不匹配类型时会触发编译错误,避免any类型的隐患。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 14:57:38