TS2559: Type '{ children: never[]; }' has no properties in common with type 'IntrinsicAttributes & { post?: IPost | undefined; }' - reactjs

I cant understand whats the probem is.
I have already saw answers to this Error here, I saw one of the issues of others
is a repetition of a function name, and not giving props to a component in the component tree. But I dont think I made those mistakes.
Ill be thankfull for help :)
the Error:
TS2559: Type '{ children: never[]; }' has no properties in common with type 'IntrinsicAttributes & { post?: IPost | undefined; }'.
28 |
29 | {posts}
30 |
| ^^^^
31 |
32 |
33 |
Feed component:
import React, { useEffect, useState } from 'react';
import { Outlet } from 'react-router';
import Post from './Post';
import {List} from './List'
async function getJSON(url: string) {
const res = await fetch(url);
return res.json();
}
export function Feed() {
const [posts, setPosts] = useState([{id: '', title: 'initial state'}])
const [page, setCount] = useState(0);
useEffect(() => {
async function setData() {
setPosts(await getJSON(`./data/posts.json?page=${page}`));
}
setData();
}, [page])
useEffect(() => {
console.log('render Feed');
console.log(posts)
})
return <div style={{backgroundColor:'#FAFAFA'}}>
<List>
{posts}
<Post>
</Post>
</List>
{/* <Post>
</Post> */}
<p>
<button onClick={() => setCount(page + 1)}>Page {page}</button>
</p>
</div>
}
Post component:
import React, { useEffect, useState } from 'react';
import { Link, useParams } from "react-router-dom";
import {List} from './List'
async function getJSON(url: string) {
const res = await fetch(url);
return res.json();
}
interface IPost {
id: string;
title: string;
}
export default function Post({ post }: { post?: IPost }) {
const [numLikes, addLike] = useState(0);
const [dynamicPost, setDynamicPost] = useState({ title: '', id: '' });
const params = useParams();
useEffect(() => {
async function setDynamicData() {
console.log('setDynamicData works')
setDynamicPost(await getJSON(`./data/post.${params.postId}.json`))
}
if (!post) {
setDynamicData();
}
console.log('post is set!')
}, [params.postId, post]);
return <div>
{post ? <div>
<PostBox>
<h2><Link style={{color: 'black', textDecoration: 'none' }} to={`/posts/${post?.id}`}>{post?.title || dynamicPost?.title}</Link></h2>
<p>Num likes {numLikes}</p>
<Like onLike={() => addLike(numLikes + 1)}>
<strong>Bla bla</strong>
</Like>
</PostBox>
</div> :
<div><h2 style={{ color: "red" }}>no posts yet</h2></div>}
</div>
}
interface IPropsLike {
onLike: () => void;
children: JSX.Element;
}
function Like({ onLike, children }: IPropsLike) {
return <button onClick={onLike}>{children}</button>
}
function PostBox({ children }: { children: JSX.Element[] }) {
return <div style={{
border: '4px solid black',
color:"black",
marginBottom: '1em',
width: '100%',
textAlign: 'center'}}>
{children}
</div>
}

The problem is you're giving Post child contents (a text node containing whitespace), but Post doesn't accept child content.
Here's a simpler replication:
interface IPost {
id: string;
title: string;
}
function Post({ post }: { post?: IPost }) {
return <div/>;
}
// v−−−− error here, because of the text node
const x = <Post post={{id: "x", title: "x"}}>
</Post>;
Playground link
Either:
Don't pass children to components that don't accept them:
<Post post={/*...*/} />
Playground link
or
Update Post so it accepts children:
export default function Post({ post, children }: { post?: IPost, children: ReactNode }) {
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^
// v−−−− no error here anymore, the text node is fine
const x = <Post post={{id: "x", title: "x"}}>
</Post>;
...and render the children in Post.
Playground link

Related

react-aria and Typescript building a popover component error with value passing

Morning, all.
I have an issue I have been struggling with for a little while. I have been using react-aria to build a popover in Storybook. This popover is built in two components, the first one is the popover itself, this works fine:
import { StyledPopover } from './PopOver.styles';
import {
DismissButton,
FocusScope,
mergeProps,
useDialog,
useModal,
useOverlay,
} from 'react-aria';
import React, { forwardRef } from 'react';
import { useObjectRef } from '#react-aria/utils';
export interface PopoverProps {
title: string;
children: React.ReactNode;
isOpen: boolean;
onClose: () => void;
}
const Popover = React.forwardRef<HTMLDivElement, PopoverProps>(
({ title, children, isOpen, onClose }, ref) => {
const forwardRef = useObjectRef(ref);
// Handle interacting outside the dialog and pressing
// the Escape key to close the modal.
const { overlayProps } = useOverlay(
{
onClose,
isOpen,
isDismissable: true,
},
forwardRef
);
// Hide content outside the modal from screen readers.
const { modalProps } = useModal();
// Get props for the dialog and its title
const { dialogProps, titleProps } = useDialog({}, forwardRef);
return (
<FocusScope restoreFocus>
<StyledPopover
{...mergeProps(overlayProps, dialogProps, modalProps)}
ref={ref}
>
<h3 {...titleProps} style={{ marginTop: 0 }}>
{title}
</h3>
{children}
<DismissButton onDismiss={onClose} />
</StyledPopover>
</FocusScope>
);
}
);
export { Popover };
Then I have the the button and the state:
import React from 'react';
import {
OverlayContainer,
useOverlayPosition,
useOverlayTrigger,
} from 'react-aria';
import { Button } from '../Button';
import { useOverlayTriggerState } from 'react-stately';
import { Popover } from 'Atoms/Popover/Popover';
export interface PopoverButtonProps {
title: string;
buttonText: string;
children: React.ReactNode;
disabled: boolean;
}
const PopoverButton = ({
buttonText,
title,
children,
disabled,
}: PopoverButtonProps) => {
const state: any = useOverlayTriggerState({});
const triggerRef = React.useRef<HTMLButtonElement>(null);
const overlayRef: any = React.useRef<HTMLDivElement>(null);
// Get props for the trigger and overlay. This also handles
// hiding the overlay when a parent element of the trigger scrolls
// (which invalidates the popover positioning).
const { triggerProps, overlayProps } = useOverlayTrigger(
{ type: 'dialog' },
state,
triggerRef
);
// Get popover positioning props relative to the trigger
const { overlayProps: positionProps } = useOverlayPosition({
targetRef: triggerRef,
overlayRef,
placement: 'top',
offset: 5,
isOpen: state.isOpen,
});
//
// const handleOnClick = (e: any) => {
// console.log(triggerProps);
// triggerProps.onPress && triggerProps.onPress(e);
// };
console.log(triggerProps);
return (
<>
<Button
onClick={(e: React.MouseEvent<HTMLInputElement>) =>
triggerProps.onPress && triggerProps.onPress(e)
}
style="secondary"
size="small"
disabled={disabled}
>
{buttonText}
</Button>
{state.isOpen && (
<OverlayContainer>
<Popover
{...overlayProps}
{...positionProps}
ref={overlayRef}
title={title}
isOpen={state.isOpen}
onClose={state.close}
>
{children}
</Popover>
</OverlayContainer>
)}
</>
);
};
export { PopoverButton };
Now, react-aria useButton takes an onPress, not an onClick.
So, in my Button component I have casted the onClick like this:
import classNames from 'classnames';
import { forwardRef } from 'react';
import { StyledButton } from './Button.styles';
import { useButton } from 'react-aria';
import { useObjectRef } from '#react-aria/utils';
import React from 'react';
export interface ButtonProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'style'> {
children: React.ReactNode;
type?: 'submit' | 'button' | 'reset';
style?: 'primary' | 'secondary' | 'icon' | 'text';
size?: 'large' | 'medium' | 'small';
block?: boolean;
disabled?: boolean;
testId?: string;
onPress?: () => void;
}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
(
{
children,
type,
style = 'primary',
size = 'large',
block = false,
disabled = false,
testId,
onPress,
...props
},
ref
) => {
const classes = classNames(style, `btn-${size}`, {
block,
});
const objRef = useObjectRef(ref);
const { buttonProps } = useButton({ onPress }, objRef);
return (
<StyledButton
{...buttonProps}
className={classes}
onClick={onPress}
type={type}
disabled={disabled}
data-testid={testId}
ref={ref}
{...props}
>
{children}
</StyledButton>
);
}
);
export { Button };
In my popoverButton component, I am passing (e) as it is required for react-aria:
onClick={(e: React.MouseEvent<HTMLInputElement>) =>
triggerProps.onPress && triggerProps.onPress(e)
}
However, I am getting these two errors -
on (e) -
Argument of type 'MouseEventHandler<HTMLButtonElement>' is not assignable to parameter of type 'PressEvent'.
Type 'MouseEventHandler<HTMLButtonElement>' is missing the following properties from type 'PressEvent': type, pointerType, target, shiftKey, and 3 more.ts(2345)
onClick -
Type '(e: React.MouseEventHandler<HTMLButtonElement>) => void | undefined' is not assignable to type 'MouseEventHandler<HTMLButtonElement>'.
Types of parameters 'e' and 'event' are incompatible.
Type 'MouseEvent<HTMLButtonElement, MouseEvent>' is not assignable to type 'MouseEventHandler<HTMLButtonElement>'.
Type 'MouseEvent<HTMLButtonElement, MouseEvent>' provides no match for the signature '(event: MouseEvent<HTMLButtonElement, MouseEvent>): void'.ts(2322)
index.d.ts(1446, 9): The expected type comes from property 'onClick' which is declared here on type 'IntrinsicAttributes & ButtonProps & RefAttributes<HTMLButtonElement>'
Now, the button works and the popup does appear, however it only disappears when clicking the button when it should dismiss when clicking anywhere on the screen. I think this is down to the issues I currently have with the onClick and onPress?
Any ideas?

React: How to solve: Property 'children' does not exist on type 'IntrinsicAttributes & Props'

I'm trying fetch data from an API and display the data into list of cards in React with typeScript. Since I am new with React in Typescript, not sure how I can solve this error or am I missing something.
This is the error I get:
Type '{ children: string[]; key: number; }' is not assignable to type 'IntrinsicAttributes & Props'.
Property 'children' does not exist on type 'IntrinsicAttributes & Props'.
This is the code:
interface Props {
pokemonItem: PokemonItem;
}
export const PokemonCardList = (props: Props) => {
const { pokemonItem } = props;
const {
id = '',
name = '',
weight = '',
height = '',
abilities = '',
} = pokemonItem;
const [pokemon, setPokemon] = React.useState<PokemonItem[]>([]);
const [loadItems, setLoadItems] = React.useState(API_URL);
const getPokemons = async () => {
setLoading(true);
const response: any = await fetch(loadItems);
const data = await response.json();
setLoadItems(data.next);
setPokemon(data.results[0].name);
setLoading(false);
const getEachPokemon = (result: any) => {
result.forEach(async (element: any) => {
const response = await fetch(
`https:pokeapi.co/api/v2/pokemon/${element.id}`
);
const data = await response.json();
// // setPokemon((currentArrayList) => [...currentArrayList, data]);
pokemon.push(data);
});
};
getEachPokemon(data.results);
await console.log(pokemon);
};
React.useEffect(() => {
return getPokemons();
}, []);
return (
<div>
{pokemon &&
pokemon.map((item, index) => (
<PokemonCard key={index}>
{item.name} {item.height} {item.weight} {item.abilities}
</PokemonCard>
))}
</div>
);
};
Thie pokemonCard component:
interface Props {
pokemonItem: PokemonItem;
}
const PokemonCard = (props: Props) => {
const { pokemonItem } = props;
const {
id = '',
name = '',
weight = '',
height = '',
abilities = '',
} = pokemonItem;
const [imageLoaded, setImageLoaded] = React.useState(false);
const urlImage = `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${id}.png?raw=true`;
return (
<div imageLoaded={imageLoaded}>
<div
src={urlImage}
onLoad={() => setImageLoaded(true)}
/>
<div>
Name: {name}
Height: {height}
Weight: {weight}
Abilities: {abilities}
</div>
</div>
);
};
Use PropsWithChildren from react:
import React, {Component, PropsWithChildren} from "react";
interface OwnProps {
foo?: BarComponent;
}
// For class component
class MyComponent extend Component<PropsWithChildren<OwnProps>> {
...
}
// For FC
const MyFunctionComponent: FC<PropsWithChildren<Props>> = ({
children,
}) => (...)
According to your definition of the PokemonCard component, you should be passing the pokemonItem as follow:
<PokemonCard pokemonItem={item} key={item.id} />
I have replaced the key prop as it is not recommended to use indexes as keys (see documentation), you could use the item's id instead. And you need to update the prop interface for the PokemonCard component so that the additional key prop doesn't break the validation:
interface Props {
pokemonItem: PokemonItem;
key: string;
}
Try this (add type for you component):
export const PokemonCardList: React.FC<Props> = (props) => {}
import React from "react";
import Paper, { PaperProps } from "#material-ui/core/Paper";
interface TableEmployeeProps extends PaperProps {
// your props
}
const TableEmployee: React.FC<TableEmployeeProps> = (props) => {
return (
<Paper {...props}> </Paper>
}

How to add static props type for ForwardRefExoticComponent

I have a Modal FC, I need to add two static methods show and hide to it. I have no idea how to add the ModalStaticProps type for the component. So that I can assign show and hide to Modal directly without using type assertion.
import React, { ReactNode, RefAttributes } from 'react';
interface ModalProps {
title: ReactNode;
}
interface ModalStaticProps {
show(): void;
hide(): void;
}
const Modal: React.ForwardRefExoticComponent<ModalProps & RefAttributes<HTMLDivElement>> = React.forwardRef<
HTMLDivElement,
ModalProps
>(({ title }: ModalProps, ref) => {
return <div ref={ref}>{title}</div>;
});
// I want to this after add `ModalStaticProps` type
// Modal.show = () => { };
// Modal.hide = () => { };
// Not this
const ModalComponent = Modal as React.ForwardRefExoticComponent<ModalProps & RefAttributes < HTMLDivElement >> & ModalStaticProps
ModalComponent.show = () => { };
ModalComponent.hide = () => { };
function Consumer() {
return <div onClick={() => ModalComponent.show()} />
}
TypeScript Playground
It is doable.
See Properties declarations on functions. It is possible to do, in a natural way, but it might break your ref types.
Hence, I decided just use Object.assign
See example:
import React, { ReactNode, RefAttributes, ForwardRefExoticComponent } from 'react';
interface ModalProps {
title: ReactNode;
}
interface ModalStaticProps {
show(): void;
hide(): void;
}
const STATIC_PROPS: ModalStaticProps = {
show: () => { },
hide: () => { }
}
const withStaticProps = <T,>(
forwarded: ForwardRefExoticComponent<ModalProps & RefAttributes<HTMLDivElement>>,
staticProps: T
) => Object.assign(forwarded, staticProps)
const Modal = React.forwardRef<
HTMLDivElement,
ModalProps
>(({ title }: ModalProps, ref) => <div ref={ref}>{title}</div>)
const ModalComponent = withStaticProps(Modal, STATIC_PROPS)
function Consumer() {
return <div onClick={() => ModalComponent.show()} />
}
Playground
See my question regarding this problem

Typing a React Component Factory Function

Given the type
type EnumerableComponentFactory = <C, I>(config: {
Container: React.ComponentType<C>;
Item: React.ComponentType<I>;
}) => React.FC<{ items: I[] }>;
with the following implementation
const Enumerable: EnumerableComponentFactory =
({ Container, Item }) =>
({ items }) =>
(
<Container>
{items.map((props, index) => (
<Item key={index} {...props} />
))}
</Container>
);
and intended use
const UnorderedList = Enumerable({
Container: ({ children }) => <ul>{children}</ul>,
Item: ({ title }: { title: string }) => <li>{title}</li>,
});
<UnorderedList items=[{title: "Something"}] />
I'm observing the following TypeScript error
Type '{ children: Element[]; }' is not assignable to type 'C'.
'C' could be instantiated with an arbitrary type which could be unrelated to '{ children: Element[]; }'.ts(2322)
which leads me to my question: What type constraints do I need to set up to resolve this error?
I've tried to change the type as follows:
type EnumerableComponentFactory = <C extends { children?: Element[] }, I>(config: {
Container: ComponentType<C>;
Item: ComponentType<I>;
}) => (props: { items: I[] }) => ReturnType<FC<I>>;
but this produces an even more cryptic error message, which I'm going to omit for the sake of brevity.
P.S. The function itself actually does exactly what's expected. It's just the compiler that trips up.
Is it necessary to keep C generic parameter?
import React, { FC, ComponentType, PropsWithChildren } from "react";
type EnumerableComponentFactory = <I>(config: {
// or Container: FC<{ children: JSX.Element[] }>;
Container: FC<PropsWithChildren<object>>;
Item: ComponentType<I>;
}) => FC<{ items: I[] }>;
const Enumerable: EnumerableComponentFactory =
({ Container, Item }) =>
({ items }) =>
(
<Container>
{items.map((props, index) => (
<Item key={index} {...props} />
))}
</Container>
);
const UnorderedList = Enumerable({
Container: ({ children }) => <ul>{children}</ul>,
Item: ({ title }: { title: string }) => <li>{title}</li>,
});
const result = <UnorderedList items={[{ title: "Something" }]} />;
I was able to alter your code to make it work, while also accepting other props to be passed to the container:
type EnumerableComponentFactory = <C, I>(config: {
Container: React.ComponentType<C & { children: React.ReactNode[] }>;
Item: React.ComponentType<I>;
}) => React.ComponentType<C & { items: I[] }>;
const Enumerable: EnumerableComponentFactory = ({ Container, Item }) => (
props
) => (
<Container {...props}>
{props.items.map((props, index) => (
<Item key={index} {...props} />
))}
</Container>
);
Which allows for e.g. this:
const ContainerWithBorder: React.ComponentType<{ color: string }> = (props) => (
<div style={{ border: `2px solid ${props.color}` }}>
<ul>{props.children}</ul>
</div>
);
const ComplexList = Enumerable({
Container: ContainerWithBorder,
Item: ({ title }: { title: string }) => <li>{title}</li>
});
<ComplexList items={[{ title: "Something" }]} color="red" />
The ComplexList component comes with typing/intellisense for the color property.
A playground with the original and ComplexList example can be found here.

React and TypeScript—which types for an Axios response?

I am trying to present a simple user list from an API which returns this:
[{"UserID":2,"FirstName":"User2"},{"UserID":1,"FirstName":"User1"}]
I do not understand fully how to handle Axios responses with types. The TypeScript error is
Type '{} | { id: number; firstName: string; }' is not assignable to type 'IntrinsicAttributes & UserListProps & { children?: ReactNode; }'.
Property 'items' is missing in type '{}' but required in type 'UserListProps'.
from the <UserList /> element in the Users.tsx file below. Is my User interface wrong?
import React, {useEffect, useState, Fragment } from 'react';
import UserList from './UserList';
import axios, {AxiosResponse} from 'axios';
interface User {
id: number;
firstName: string;
}
const Users: React.FC = (props) => {
const [users, setUserList] = useState<User>();
useEffect(() => {
// Use [] as second argument in useEffect for not rendering each time
axios.get('http://localhost:8080/admin/users')
.then((response: AxiosResponse) => {
console.log(response.data);
setUserList( response.data );
});
}, []);
return (
<Fragment>
<UserList {...users} />
</Fragment>
);
};
export default Users;
Below is my UserList.tsx.
import React, {Fragment } from 'react';
interface UserListProps {
items: {id: number, firstName: string}[];
};
const UserList: React.FC<UserListProps> = (props) => {
return (
<Fragment>
<ul>
{props.items.map(user => (
<li key={user.id}>
<span>{user.firstName}</span>
{/* not call delete function, just point to it
// set this to null in bind() */}
</li>
))}
</ul>
</Fragment>
);
};
export default UserList;
There is generic get method defined in axios/index.d.ts
get<T = never, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig<T>): Promise<R>;
Example
interface User {
id: number;
firstName: string;
}
axios.get<User[]>('http://localhost:8080/admin/users')
.then(response => {
console.log(response.data);
setUserList( response.data );
});
I think you are passing list the wrong way to child component.
const [users, setUserList] = useState<User[]>([]);
<UserList items={users} />
interface UserListProps {
items: User[];
};
const UserList: React.FC<UserListProps> = ({items}) => {
return (
<Fragment>
<ul>
{items.map(user => (
<li key={user.id}>
<span>{user.firstName}</span>
</li>
))}
</ul>
</Fragment>
);
};
You need to provide a type argument when calling axios.get if you do not want Axios to infer the type for the value response as any.
And you are passing an incorrect type argument when you useState to create the array of users.
The correct way
interface User {
id: number;
firstName: string;
}
// Initialized as an empty array
const [users, setUserList] = useState<User[]>([]); // 'users' will be an array of users
For example,
import React, {useEffect, useState, Fragment } from 'react';
import UserList from './UserList';
import axios from 'axios';
interface User {
id: number;
firstName: string;
}
// You can export the type TUserList to use as -
// props type in your `UserList` component
export type TUserList = User[]
const Users: React.FC = (props) => {
// You can also use User[] as a type argument
const [users, setUserList] = useState<TUserList>();
useEffect(() => {
// Use [] as a second argument in useEffect for not rendering each time
axios.get<TUserList>('http://localhost:8080/admin/users')
.then((response) => {
console.log(response.data);
setUserList(response.data);
});
}, []);
return (
<Fragment>
<UserList {...users} />
</Fragment>
);
};
export default Users;
If you choose to export the type type TUserList = User[], you can use it in your UserList component as the type for props. For example,
import React, {Fragment } from 'react';
import { TUserList } from './Users';
interface UserListProps {
items: TUserList // Don't have to redeclare the object again
};
const UserList: React.FC<UserListProps> = (props) => {
return (
<Fragment>
<ul>
{props.items.map(user => (
<li key={user.id}>
<span>{user.firstName}</span>
{ /* Do not call the delete function. Just point
to it. Set this to null in bind(). */}
</li>
))}
</ul>
</Fragment>
);
};
export default UserList;

Resources