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

React Context内使用keyof时TypeScript类型推断失效问题

TypeScript Context 类型推断异常问题

问题现象

通过keyof实现了根据对象键名推断对应值类型的规则,该规则在React Context Provider外部调用时类型推断完全正常,但在Context Provider内部实现同类函数时出现推断错误:调用Context暴露的getSportInfo方法传入具体键名时,返回值被推断为所有可能值的联合类型,无法窄化为对应键的具体类型。

复现代码

type IndividualSport = {
  name: string;
}

type TeamSport = {
  name: string;
  players: number;
}

type Sports = {
  cricket: TeamSport;
  golf: IndividualSport;
}

type SportsKey = keyof Sports;

let sampleSports: Sports = {
  cricket: {
    name: "Cricket",
    players: 11
  },
  golf: {
    name: "Golf",
  },
};

function extractSport<K extends SportsKey>(sports: Sports, key: K) {
  return sports[key];
}

const cricket = extractSport(sampleSports, 'cricket');
const golf = extractSport(sampleSports, 'golf');

// 此处类型推断正常
console.log(cricket.players);
console.log(golf.name);

// REACT CONTEXT PROVIDER 相关逻辑

type SportsContextProps<K extends SportsKey> = {
  getSportInfo: (key: K) => Sports[K];
}

const SportsContext = createContext<SportsContextProps<SportsKey>>({
  getSportInfo: (key) => extractSport(sampleSports, key),
});

const useSportsContext = () => useContext(SportsContext)!;

function SportsProvider({
  children,
}: PropsWithChildren<{}>): ReactElement {

  const [sports, setSports] = useState<Sports>(sampleSports);

  function getSportInfo(key: SportsKey): Sports[SportsKey] {
    return extractSport(sports, key);
  }

  return (
    <SportsContext.Provider
      value={{
        getSportInfo,
      }}
    >
      {children}
    </SportsContext.Provider>
  );
}

function App() {
  const { getSportInfo } = useSportsContext();
  const cricket = getSportInfo('cricket');

  console.log(cricket.name);
  console.log(cricket.players); // 类型校验失败:cricket被推断为TeamSport | IndividualSport联合类型

  return (
    <div>App</div>
  );
}

ReactDOM.render(
  <React.StrictMode>
    <SportsProvider>
      <App />
    </SportsProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

问题原因

核心错误是泛型定义位置错误:

  • 外部的extractSport是泛型函数,调用时会根据传入的key自动推导泛型参数K的具体类型,因此可以正确窄化返回值。
  • 原代码将泛型参数定义在SportsContextProps类型上,初始化Context时直接传入SportsKey作为固定泛型参数,导致getSportInfo被推导为参数类型是'cricket' | 'golf'联合类型、返回值是TeamSport | IndividualSport联合类型的普通函数,完全丢失了调用时根据入参窄化返回值的泛型能力。

修复方案

将泛型参数从Context整体类型移到内部的getSportInfo方法上,让getSportInfo本身成为泛型函数即可:

  • 调整Context类型定义
// 移除类型上的泛型,将泛型定义在getSportInfo方法上
type SportsContextProps = {
  getSportInfo: <K extends SportsKey>(key: K) => Sports[K];
}
  • 修正Context初始化逻辑,不再传入固定泛型参数
const SportsContext = createContext<SportsContextProps>({
  getSportInfo: (key) => extractSport(sampleSports, key),
});
  • 调整Provider内的方法定义,匹配泛型函数签名
function SportsProvider({
  children,
}: PropsWithChildren<{}>): ReactElement {
  const [sports, setSports] = useState<Sports>(sampleSports);

  // 定义为泛型函数,不需要手动写死参数和返回值的联合类型
  function getSportInfo<K extends SportsKey>(key: K) {
    return extractSport(sports, key);
  }

  return (
    <SportsContext.Provider value={{ getSportInfo }}>
      {children}
    </SportsContext.Provider>
  );
}

修复后调用getSportInfo('cricket')时,TS会自动推断返回值为TeamSport类型,访问players属性不会再报类型错误。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 18:57:33