[英]Typescript and React: How to dynamically infer types from the parent component?
[英]TypeScript types to infer via keyof within React Context Provider
我已设置 TypeScript 类型以使用 keyof 基于对象键推断值。 它们似乎在 React 上下文提供程序之外工作,但是在上下文提供程序中使用类似功能时我遇到了问题。 TypeScript Playground 链接最好地演示了推理错误。
我想我在某处缺少类型注释,但找不到任何有效的解决方案。 任何帮助表示赞赏!
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') );
首先,您不必创建IndividualSport
和TeamSport
。 您可以创建交叉点类型:
type Sport = {
name: string;
players?: number; // don't forget the question mark
};
但如果你有意创建两种类型,只需扩展一个接口:
interface IndividualSport {
name: string;
}
interface TeamSport extends IndividualSport {
players: number;
}
但是在您的情况下,您只需要一种类型Sport
。 因此,您的Sports
类型变为:
type Sports = {
cricket: Sport;
golf: Sport;
};
接下来,我认为我们不必制作关键的泛型。 如果你写 SportsKey is keyof
Sports,则SportsKey
已经是Sports
键的字符串或数字文字并集
type SportsKey = keyof Sports;
// idem with:
type SportsKey = 'name' | 'players'
因此,您的上下文创建变为:
function extractSport(sports: Sports, key: SportsKey) {
return sports[key];
}
type SportsContextProps = {
getSportInfo: (key: SportsKey) => Sports[SportsKey];
};
const SportsContext = createContext<SportsContextProps>({
getSportInfo: (key: SportsKey) => extractSport(sampleSports, key),
});
const useSportsContext = () => useContext(SportsContext);
function SportsProvider({ children }: PropsWithChildren<{}>): ReactElement {
const [sports] = useState<Sports>(sampleSports);
function getSportInfo(key: SportsKey): Sports[SportsKey] {
return extractSport(sports, key);
}
return (
<SportsContext.Provider
value={{
getSportInfo,
}}
>
{children}
</SportsContext.Provider>
);
}
最后,您可以访问您的运动上下文并记录结果,如下所示:
function App() {
const sports = useSportsContext();
useEffect(() => {
if (sports) {
const cricket = sports.getSportInfo("cricket");
console.log(cricket);
}
}, [sports]);
return (
<SportsProvider>
<div>App</div>
</SportsProvider>
);
}
但是,如果您仍想使用您的方法,简短的解决方案是您需要将cricket
转换为TeamSport
,即使板球的类型已经:
const cricket: TeamSport | IndividualSport
// which is comes from:
const getSportInfo: (key: keyof Sports) => IndividualSport | TeamSport
您的方法的缺点是,您总是会被要求手动投射板球和高尔夫,以告诉 typescript 将使用哪种类型。
function App() {
const { getSportInfo } = useSportsContext();
useEffect(() => {
const cricket = getSportInfo("cricket");
const { name, players } = cricket as TeamSport;
console.log(name, players);
}, [])
return (
<div>App</div>
);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.