javascript - Legge til typedefinisjoner til en HOC som injiserer props i en komponent
Jeg har en "higher order component" som er implementert på følgende måte (uten typer).
const Themeable = (mapThemeToProps) => {
return (WrappedComponent) => {
const themedComponent = (props, { theme: appTheme }) => {
return <WrappedComponent
{...props}
theme={merge(
{},
defaultTheme,
appTheme,
mapThemeToProps(merge(defaultTheme, appTheme))
)}
>
}
themedComponent.contextTypes = { theme: PropTypes.object };
return themedComponent;
}
}
For å oppsummere hva det gjør, tar det en mapThemeToProps-funksjon. Denne vil motta en theme-parameter som er opprettet ved å slå sammen defaultTheme (tilveiebrakt av biblioteket mitt) og appTheme (tilveiebrakt av en ThemeProvider-komponent via kontekst). Den vil deretter lage et utvidet Theme og sende det til komponenten som en prop kalt theme. I praksis vil det bli brukt som følger (kalt Themeable i dette omfanget):
const mapThemeToProps = (theme) => ({
background: theme.palette.dark,
color: theme.text.light,
});
function Greeting({ theme: { background, color } }) {
return (
<div style={{ background, color }}>
Hello World!
<div>
)
}
export default Themeable(mapThemeToProps)(Greeting);
Jeg har veldig vanskelig for å utvikle en riktig typisering for denne funksjonen. Jeg har funnet ut at dette mønsteret er veldig likt noe i retning av connect fra react-redux, så jeg har jobbet ut fra deres typinger som utgangspunkt. Uansett, jeg er litt fortapt, dette er egentlig hvor jeg er nå:
import { Theme } from 'types/Theme';
interface ThemeableComponentEnhancer {
<P>(component: React.ComponentType<P>): React.ComponentClass<Pick<P, "theme"> & { theme: Theme }> & { WrappedComponent: React.Component<P>}
}
export interface Themeable {
// calling it with no parameters returns a basic theme.
(): Theme;
// calling it with a function
<TTheme = {}>(mapThemeToProps: MapThemeToPropsParam<TTheme>): ComponentEnhancer<{ theme: TTheme }>;
}
interface MapThemeToProps<TTheme> {
(theme: TTheme): TTheme;
}
interface MapThemeToPropsFactory<TTheme> {
(theme: Theme): MapThemeToProps<TTheme>;
}
type MapThemeToPropsParam<TTheme> = MapStateToPropsFactory<TTheme>
Jeg har problemer med å få hodet rundt dette. Hvordan ville dette bli gjort i TypeScript?
Svar #1
Theme.ts
export interface Theme {
palette: {
primary: string;
secondary: string;
};
}
export const theme: Theme = {
palette: {
primary: 'red',
secondary: 'yellow'
}
};
Eksempel på Rect-komponent med Theme
import * as React from 'react';
import { Theme } from '../theme/Theme';
import withTheme, { MapThemeToProps } from '../theme/withTheme';
export interface RectThemeProps {
titleColor?: string;
bodyColor?: string;
}
export interface RectProps extends RectThemeProps {
content?: string;
}
export class Rect extends React.Component<RectProps> {
render() {
const {titleColor, bodyColor, content} = this.props;
return <div>{content && content}</div>;
}
}
const mapThemeToProps: MapThemeToProps<
RectThemeProps,
Theme
> = (theme) => {
return {
titleColor: theme.palette.primary,
bodyColor: theme.palette.secondary
};
};
export default withTheme(mapThemeToProps)(Rect);
withTheme.ts
import * as React from 'react';
const ID = '__context_id__';
export interface MapThemeToProps<M, T> {
(theme: T): M;
}
export interface withTheme {
<M, T>(mapThemeToProps: MapThemeToProps<M, T>):
(c: React.ComponentType<M>) => React.ComponentType<M>;
}
export default <M, T>(
mapThemeToProps: MapThemeToProps<M, T>
) => (Component: React.ComponentType<M>) => {
return class extends React.Component<M> {
render() {
const theme = this.context[ID];
return (
<Component
{...this.props}
{...mapThemeToProps(theme.getTheme())}
/>
);
}
};
}
