問題

我正在將React應用程式轉換為使用大量函式程式設計模式的Typescript.其中最重要的模式是能夠組合多個更高階元件以返回增強元件.我想要發生的是讓Typescript在每個級別推斷元件的型別並使用我傳遞的元件完成以上所有型別的HOC.

我在3.5.0和最新版本的Typescript上嘗試了這段程式碼.在程式碼方面,我嘗試使通用擴充套件我想要的型別<T extends FooProps>,將FC<T & BarProps>傳遞給返回函式元件,並扭轉HOC的呼叫.

 export const withFoo = <T>(Component: FC<T & FooProps>): FC<Omit<T, 'foo'>> => props => (
  <Component {...props as T} foo="foo" />
)

export const withBar = <T>(Component: FC<T & BarProps>): FC<Omit<T, 'bar'>> => props => (
  <Component {...props as T} bar="bar" />
)

export const EnhancedComponent = withFoo(
  withBar(({ bar, foo }) => { // bar exists and foo does not exist on type 'PropsWithChildren<BarProps>'
    console.log(bar) // types come through (bar: string)
    console.log(foo) // types don't come through (foo: any) :(
    return <div>woohoo</div> 
  }),
)
 

我希望在HOC的每個級別都會遇到型別.在上面的例子中,我想在函式元件中看到這個我傳遞:

 export const EnhancedComponent = withFoo(
  withBar(({ bar, foo }) => { // both exist on the component
    console.log(bar) // bar: string
    console.log(foo) // foo: string
    return <div>woohoo</div>
  }),
)
 

  最佳答案

尋找高階元件打字的好地方是其他包或@types包。

但是,您的用例有點有趣,因為您希望包裝的元件接收更多的道具,每個包裝高階元件提供其中一個值。

鑑於這一點,這是一個潛在的解決方案.有幾點需要指出:

  • 假設您想要允許任何元件型別(類或函式),達到 React.ComponentType 可能是您想要的.
  • 我嘗試避免播放,但看起來這仍然是TypeScript的一個缺點(問題).這兩個問題看起來相關:#28884 & #28748
  • 新增Omit作為參考,因為在3.5之前它不存在(我在Codesandbox中測試,這時仍在3.3)
 import * as React from "react";
import { render } from "react-dom";

// This exists in TypeScript 3.5+
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

interface FooProps {
  foo: string;
}

interface BarProps {
  bar: string;
}

export const withFoo = <Props extends FooProps>(
  Component: React.ComponentType<Props>
): React.ComponentType<Omit<Props, keyof FooProps>> => props => (
  <Component {...props as Props} foo="foo" />
);

export const withBar = <Props extends BarProps>(
  Component: React.ComponentType<Props>
): React.ComponentType<Omit<Props, keyof BarProps>> => props => (
  <Component {...props as Props} bar="bar" />
);

const WrappedComponent = ({ bar, foo, more }) => {
  console.log(more);
  console.log(bar);
  console.log(foo);
  return <div>woohoo</div>;
};

const EnhancedComponent = withFoo(withBar(WrappedComponent));

const App = () => <EnhancedComponent more="more" />;

const rootElement = document.getElementById("root");
render(<App />, rootElement);