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
相关产品推荐
相关产品推荐

