Search code examples
reactjstypescriptreact-typescript

Property Y does not exist on type 'FunctionComponent<Interface>' even though Interface contains property Y


I have 3 components, Grid, BlockWrapper, and Block.

Block is a children of BlockWrapper.

Block has the props blockClassName and text, which I specified in an interface:

Grid/index.tsx

    const blockWrappers:Array<JSX.Element> = [];
    const blocks:Array<JSX.Element> = [];
    
    // ...

    for(let i = 0; i < 16; i++) {
        blocks.push(
            <Block
                blockClassName={ blockClassName }
                text={i + ""}
            />
        );
        
        blockWrappers.push(
            <BlockWrapper index={i} >
                { blocks[i] }
            </BlockWrapper>
        )
    }

    // ...


    interface BlockProps {
        blockClassName: string,
        text: string
    }
    
    swappable.on("swappable:swapped", () => {
        for(let wrapper of blockWrappers) { // blockWrappers is an array of BlockWrapper
            let child = wrapper.props.children as React.FunctionComponent<BlockProps>; // Getting child of BlockWrapper, which is Block
            if(child.blockClassName) console.log(); // Error: Property 'blockClassName' does not exist on type 'FunctionComponent<BlockProps>'.ts(2339)
        }
        updateClasses();
    });

Block/index.tsx

const Block = ( {blockClassName, text}: {blockClassName: string, text: string} ) => {
    if(blockClassName.includes("isEmpty")) text = "";
    return (
        <span className={blockClassName}>
            <Content>
                <h1>{text}</h1>
            </Content>
        </span>
    );
}

However, when I try to access blockClassName from child, which is a FunctionComponent, it gives me an error saying the property "blockClassName" does not exist in child. I already specified in BlockProps that it has a property called "blockClassName," and I don't see why the error is showing up. How can I fix this?

Thanks in advance.


Solution

  • In what you've shown, blockWrappers is an array of React.ReactElement, each with a single child. That's not React.FunctionComponent<BlockProps>, which is a function type, it's React.ReactElement<BlockProps>.

    You can access that single child via React.Children.only (if there were more, you'd use one of the other methods on React.Children)):

    const child = React.Children.only<React.ReactElement<BlockProps>>(wrapper.props.children);
    

    and then just as with the wrapper, you need to use .props to see the child's props:

    console.log("blockClassName = ", child.props.blockClassName);
    

    E.g.:

    for (const wrapper of blockWrappers) {
        const child = React.Children.only<React.ReactElement<BlockProps>>(wrapper.props.children);
        if (child.props.blockClassName) {
            console.log("blockClassName = ", child.props.blockClassName);
        }
    }
    

    Playground link showing the types (a simplified version I created before your edit)