Skip to content

Instantly share code, notes, and snippets.

@karol-majewski
Last active April 30, 2024 16:01
Show Gist options
  • Save karol-majewski/d2fe4a9f60af8f8b692962f06ea13e32 to your computer and use it in GitHub Desktop.
Save karol-majewski/d2fe4a9f60af8f8b692962f06ea13e32 to your computer and use it in GitHub Desktop.
Functional composition for React props
import * as React from 'react';
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
/**
* Supplies default props.
*/
export function withDefaults<T extends React.ComponentType<any>, U extends Partial<React.ComponentProps<T>>>(
Component: T,
defaults: U,
): React.FunctionComponent<Omit<React.ComponentProps<T>, keyof U> & Partial<U>> {
// Returning `<Component { ...defaults} {...props } />;` gives an error.
// @see https://github.com/Microsoft/TypeScript/issues/14729
return props => React.createElement(Component, { ...defaults, ...props });
}
/**
* Partially applies props.
*/
export function withProps<T extends React.ComponentType<any>, U extends Partial<React.ComponentProps<T>>>(
Component: T,
defaults: U,
): React.FunctionComponent<Omit<React.ComponentProps<T>, keyof U>> {
return props => React.createElement(Component, { ...defaults, ...props });
}
@karol-majewski
Copy link
Author

Usage:

/**
 * Example usage.
 */
interface Props {
  name: string;
  age: number;
}

declare const Foo: React.FunctionComponent<Props> | React.ComponentClass<Props>;

const FooWithDefaults = withDefaults(Foo, { name: 'Bob' });

<Foo age={0} name="Bob" />;              // Both props must be provided.
<FooWithDefaults age={0} />;             // No need to provide `name`!
<FooWithDefaults age={0} name="Hank" />; // It's still possible to override the defaults.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment