I'm new to TypeScript with React and I have (already) two HOCs in React and I would like to compose
them because it is likely that there will be more.
import { getIsAuthenticated } from 'features/user-authentication/user-authentication-reducer';
import { connect, ConnectedProps } from 'react-redux';
import { RootState } from 'redux/root-reducer';
import { withTranslation } from '../../../i18n';
import HomePageComponent from './home-page-component';
const mapStateToProps = (state: RootState) => ({
isAuthenticated: getIsAuthenticated(state),
});
const connector = connect(mapStateToProps);
export type PropsFromRedux = ConnectedProps<typeof connector>;
export default withTranslation()(connector(HomePageComponent));
That is how it currently looks (and works / compiles). The component is:
// ...
import { TFunction } from 'next-i18next';
// ...
import { PropsFromRedux } from './home-page-container';
type Props = {
readonly t: TFunction;
} & PropsFromRedux;
const HomePageComponent = ({ isAuthenticated, t }: Props) => (
// ...
In JavaScript I usually do:
export default compose(withTranslation(), connector)(HomePageComponent);
where compose
is either from Ramda or from Redux itself.
Using Ramda's compose
throws two errors.
connector
:No overload matches this call.
HomePageComponent
:Expected 0 arguments, but got 1.
Using Redux' compose
compiles here, but breaks my tests.
import { render, screen } from 'tests/test-helpers';
import HomePageContainer from './home-page-container';
describe('Home Page Container', () => {
it('should render a greeting', () => {
render(<HomePageContainer />);
expect(screen.getByText(/hello world/i)).toBeInTheDocument();
});
});
In the test file, HomePageContainer
throws:
JSX element type 'HomePageContainer' does not have any construct or call signatures.
How can I use compose
for higher-order components and get it to work with TypeScript?
PS: I found this other question and answer in SO, but the answer doesn't work in the latest TS versions and its only for two HOCs, I need to compose more.
My tsconfig.json
:
{
"compilerOptions": {
"allowJs": true,
"baseUrl": "./src",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"jsx": "preserve",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "es5"
},
"exclude": ["node_modules"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
}
Minimal example in CodeSandbox: https://codesandbox.io/s/hoc-breaking-example-mtp3m
If you go to rambda
compose
typing you'll see that it's generic, so you can define what would be the resulting function, typing it like this removes the error