skip to Main Content

I am new to Typescript and trying to mimic styled-components at a much lower scale. In my case, I want to make children required if user passed true in createStyledComponent for children parameter, otherwise make the children optional.

I kinda have a newbie solution to my problem where I am returning 2 different functionsm, one with required children and other with optional.

Is there a better and concise way to do this task?

const createStyledComponent = (
    type: any,
    component_style: CSSProperties | undefined,
    children?: boolean
) => {
    if (children) {
        const returnComponent = ({
            children,
            style,
        }: {
            children: any;
            style?: CSSProperties | undefined;
        }) => {
            return React.createElement(
                type,
                { style: { ...component_style, ...style } },
                children
            );
        };
        return returnComponent;
    } else {
        const returnComponent = ({
            children,
            style,
        }: {
            children?: any;
            style?: CSSProperties | undefined;
        }) => {
            return React.createElement(
                type,
                { style: { ...component_style, ...style } },
                children
            );
        };
        return returnComponent;
    }
};

2

Answers


  1. I would probably try and take a stab at generics honestly. https://www.typescriptlang.org/docs/handbook/2/generics.html

    Something like the following might work for your use case.

    type StyledProps<C extends boolean> = {
        style?: CSSProperties;
        children: C extends true ? ReactNode : ReactNode | undefined;
    };
    
    const createStyledComponent = <C extends boolean>(
        type: any,
        component_style?: CSSProperties,
        childrenRequired: C = false as C
    ) => {
        const returnComponent = ({ children, style }: StyledProps<C>) => {
            return React.createElement(
                type,
                { style: { ...component_style, ...style } },
                children
            );
        };
        return returnComponent;
    };
    

    But your implementation details might vary. I definitely would stray away from big if, else render blocks that are largely the same. In that type of scenario it can be helpful to abstract these blocks into separate components, or do inline conditionals

    return (
        <div>
            {/* other shared render logic */}
            {value === true && (
                <div>value true render</div>
            )}
            {value === false && (
                <div>value false render</div>
            )}
        </div>
    )
    

    Good luck!

    Login or Signup to reply.
  2. You want function overloads.


    You have two signatures here:

    One where children are required:

    function createStyledComponent(
      type: any,
      component_style: CSSProperties | undefined,
      children: true, // explicit true
    ): (
      props: { children: React.ReactNode, style?: CSSProperties }
    ) => React.ReactNode
    

    And one where they are not:

    function createStyledComponent(
      type: any,
      component_style: CSSProperties | undefined,
      children?: false // missing or explicit false
    ): (
      props: { children?: React.ReactNode, style?: CSSProperties }
    ) => React.ReactNode
    

    Then you can write the implementation as follows:

    function createStyledComponent(
        type: any,
        component_style: CSSProperties | undefined,
        // children parameter here is not actually required,
        // since the implementation is identical
    ): (
      props: { children?: React.ReactNode, style?: CSSProperties }
    ) => React.ReactNode {
        const returnComponent = ({
            children,
            style,
        }: {
            children?: React.ReactNode;
            style?: CSSProperties | undefined;
        }) => {
            return React.createElement(
                type,
                { style: { ...component_style, ...style } },
                children
            );
        };
        return returnComponent;
    };
    

    Note that the children parameter to your function is gone in the implementation. It has no use at runtime since the logic is identical, but it does allows us to give the type system a tip and enforce what is required.

    Now to test that out:

    createStyledComponent('div', undefined)({}) // fine
    createStyledComponent('div', undefined)({ children: <></> }) // fine
    
    createStyledComponent('div', undefined, true)({ children: <></> }) // fine
    createStyledComponent('div', undefined, true)({}) // error as expected, no children
    

    See playground

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search