I have setup the TypeScript types to infer the value based on an object key using keyof. They appear to work outside of the React Context Provider however I am having problems when using similar functions inside the context provider. The TypeScript Playground link demonstrates the inference error best.
I figure I am missing Type annotations somewhere but cannot find any solutions that work. Any assistance appreciated!
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');
// THIS WORKS
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();
// Why is cricket not inferred correctly from the context?
const cricket = getSportInfo('cricket');
console.log(cricket.name);
console.log(cricket.players); // THIS FAILS as cricket is inferred as TeamSport | IndividualSport
return (
<div>App</div>
);
}
ReactDOM.render(
<React.StrictMode>
<SportsProvider>
<App />
</SportsProvider>
</React.StrictMode>,
document.getElementById('root')
);