繁体   English   中英

要通过 React Context Provider 中的 keyof 推断的 TypeScript 类型

[英]TypeScript types to infer via keyof within React Context Provider

我已设置 TypeScript 类型以使用 keyof 基于对象键推断值。 它们似乎在 React 上下文提供程序之外工作,但是在上下文提供程序中使用类似功能时我遇到了问题。 TypeScript Playground 链接最好地演示了推理错误。

我想我在某处缺少类型注释,但找不到任何有效的解决方案。 任何帮助表示赞赏!

TypeScript 游乐场演示

 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') );

首先,您不必创建IndividualSportTeamSport 您可以创建交叉点类型

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>
  );
}

最后,您可以访问您的运动上下文并记录结果,如下所示:

编辑 React Typescript(分叉)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM