Property 'children' is missing in type
Asked Answered
S

5

23

I am trying to setup Storybook with Typescript using babel-loader and ts-loader.

Everything works fine except using children in a React component:

[tsl] ERROR in .../stories/index.stories.tsx(8,56)
      TS2769: No overload matches this call.
      Property 'children' is missing in type '{ title: string; }' but required in type 'Props'.

This is the .storybook/main.js file:

module.exports = {
  addons: [
    "@storybook/addon-knobs",
  ],
  stories: ["../packages/**/*.stories.tsx"],
  webpackFinal: async config => {
    config.module.rules.push({
      test: /\.(ts|tsx)$/,
      exclude: /node_modules/,
      use: [
        {
          loader: require.resolve('ts-loader')
        },
        {
          loader: require.resolve('babel-loader'),
          options: {
            presets: [
              "@babel/preset-env",
              "@babel/preset-react"
            ]
          }
        }
      ],
    });

    config.resolve.extensions.push('.ts', '.tsx');

    return config;
  }
};

This is the index.stories.tsx file:

import React from "react";

import Collapsible from "../src";

export default {
  title: "Collapsible"
};

const content = <span>Content</span>;

export const simpleCollapsible = () => (
  <Collapsible title="Collapsible">{content}</Collapsible>
);

And this is the implementation of Collapsible:

import React, { ReactNode, useState } from "react";
import styled, { ThemeProvider } from "styled-components";
import {
  BorderProps,
  ColorProps,
  FlexboxProps,
  LayoutProps,
  SpaceProps,
  TypographyProps
} from "styled-system";
import Theme from "@atw/theme";

import { KeyboardArrowDown } from "@styled-icons/material";

import Box from '~/box';
import Button from '~/button';

interface Props
  extends BorderProps,
    ColorProps,
    FlexboxProps,
    LayoutProps,
    SpaceProps,
    TypographyProps {
  children: ReactNode;
  title: string;
}

const Collapsible = ({ children, title, ...rest }: Props) => {
  const [isCollapsed, setIsCollapsed] = useState(false);

  const handleCollapse = () => {
    setIsCollapsed(!isCollapsed);
  };

  return (
    <ThemeProvider theme={Theme}>
      <Button
        border="none"
        padding={0}
        marginBottom={2}
        width={1}
        textAlign="start"
        onClick={handleCollapse}
        {...rest}
      >
        <IconBox isCollapsed={isCollapsed}>
          <KeyboardArrowDown size="24px" />
        </IconBox>
        {title}
      </Button>
      {!isCollapsed && (
        <Box display="flex" flexDirection="column">
          {children}
        </Box>
      )}
    </ThemeProvider>
  );
};

export default Collapsible;

Is there anything here I'm doing wrong?

Sumikosumma answered 24/3, 2020 at 23:8 Comment(5)
Could you please also paste the implementation for Collapsible.Inkstand
@ShaunLuttin Hi Shaun, I've edited my question :)Sumikosumma
A bit cumbersome to debug without a working sandbox, but have you tried to not explicitly define children? E.g. by using the default mechanism in React.FC as in codesandbox.io/s/intelligent-http-rj108? For your case it would imply the following changes: edit const Collapsible: React.FC<Props> = ({ children, title, ...rest }) => { and remove children: ReactNode; from type definitionClemenciaclemency
Hi @Gustav, could you post this as an answer so you could get the bounty since you helped me first on this issue with a viable solution please? This is not the solution that I used because I want to specifically declare the children to the functional components that requires them. But since it worked I'd prefer that you get the bounty :)Sumikosumma
@Sumikosumma Done! :-)Clemenciaclemency
C
17

One possible solution is to leverage the default children mechanism in functional components, React.FC, which lets you mount children without explicitly include them as a prop in your type definition. For your case this is achievable by applying the following changes:

interface Props
  extends BorderProps,
    ColorProps,
    FlexboxProps,
    LayoutProps,
    SpaceProps,
    TypographyProps {
  title: string;
}

const Collapsible: React.FC<Props> = ({ children, title, ...rest }) => {
  ...
};

Working sandbox for this

Clemenciaclemency answered 28/3, 2020 at 19:15 Comment(1)
This comes up with errors for meBagley
A
10

step 1

add React.FC: it already comes with children declared for you. And add your custom Props inside the React's.

Your code should be this:

const Collapsible : React.FC<Props> = ({ children, title, ...rest }) => {

step 2

interface Props extends BorderProps,
    ColorProps,
    FlexboxProps,
    LayoutProps,
    SpaceProps,
    TypographyProps {
  children: ReactNode; // as React.FC declares it for you, just delete this line
  title: string;
}
Amblygonite answered 28/3, 2020 at 8:0 Comment(0)
H
2

Have you tried

import { PropsWithChildren } from 'react';

and in your component, wrap the props with it.

    const Collapsible: PropsWithChildren<Props> = ({ children, title, ...rest }) => {
  ...
};
Handgrip answered 2/4, 2023 at 13:11 Comment(0)
S
0

I added the Babel Preset Typescript and the error is gone.

This is what my .storybook/main.js looks like now:

module.exports = {
  addons: [
    "@storybook/addon-knobs",
  ],
  stories: ["../packages/**/*.stories.tsx"],
  webpackFinal: async config => {
    config.module.rules.push({
      test: /\.(ts|tsx)$/,
      exclude: /node_modules/,
      use: [
        {
          loader: require.resolve('ts-loader'),
          options: {
            configFile: '../tsconfig.json',
            transpileOnly: true
          }
        },
        {
          loader: require.resolve('babel-loader'),
          options: {
            presets: [
              "@babel/preset-env",
              "@babel/preset-react",
              "@babel/preset-typescript"
            ]
          }
        }
      ],
    });

    config.resolve.extensions.push('.ts', '.tsx');

    return config;
  }
};
Sumikosumma answered 28/3, 2020 at 18:24 Comment(0)
K
0

In my case for error in I used it like this, if this helps:

<NavigationContainer
      children={
        <Stack.Navigator initialRouteName="Home">
          <Stack.Screen
            name="Home"
            component={Home}
            options={{ title: "Trendings Products" }}
          />
          <Stack.Screen
            name="Details"
            component={Detail}
            options={{ title: "Product Details" }}
          />
        </Stack.Navigator>
      }
    />
Kick answered 5/12, 2023 at 5:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.