I am adding some conditional styling depending on the the href
type. For some reason if I add my as
to the Button
styled-component
I get the TS error: Type 'string' is not assignable to type 'undefined'. TS2769
interface LinkProps {
to: string;
target?: string;
as?: string;
className?: string;
children: ReactChildren | React.ReactNode | string;
}
const Button = styled(RLink)`
text-decoration: none;
`;
export const Link = ({
to,
target,
children,
as = 'a',
className
}: LinkProps) => {
return (
<Button
as={as}
to={to}
target={target}
rel="noopener"
className={className}
>
{children}
</Button>
);
};
as
:as
:index.js:1 /frontend/claim-handler/src/components/shared/Link/index.tsx
TypeScript error in /frontend/claim-handler/src/components/shared/Link/index.tsx(39,6):
This JSX tag's 'children' prop expects type 'never' which requires multiple children, but only a single child was provided. TS2745
37 | }: LinkProps) => {
38 | return (
> 39 | <Button
| ^
40 | as={as}
41 | to={to}
42 | target={target}
console.<computed> @ index.js:1
handleErrors @ webpackHotDevClient.js:174
push.../../node_modules/react-dev-utils/webpackHotDevClient.js.connection.onmessage @ webpackHotDevClient.js:213
index.js:1 /frontend/claim-handler/src/components/shared/Link/index.tsx
TypeScript error in /frontend/claim-handler/src/components/shared/Link/index.tsx(40,7):
No overload matches this call.
Overload 1 of 2, '(props: Omit<Omit<import("/node_modules/@types/react-router-dom/index").LinkProps<unknown> & RefAttributes<HTMLAnchorElement> & LinkProps, never> & Partial<...>, "theme"> & { ...; } & { ...; }): ReactElement<...>', gave the following error.
Type 'string' is not assignable to type 'undefined'. TS2769
38 | return (
39 | <Button
> 40 | as={as}
| ^
41 | to={to}
42 | target={target}
43 | rel="noopener"
By removing the option as, which I don't agree with, as aren't these Types what the component expects to recieve as Props, not just arguments?
Here are the new errors:
The main problem is you're trying to pass extremely wide type string
to the prop accepting quite a big but still exactly defined set of strings as names of native html tags.
If you're going to pass as as
prop only the names of html
tags (i.e. not other functional or class components) you may type as
like that:
interface LinkProps {
to: string;
target?: string;
as?: keyof JSX.IntrinsicElements;
className?: string;
children: ReactChildren | React.ReactNode | string;
}
That should solve your problem.
And another minor issue.
While it's completely ok to type children
prop yourself. That's pretty brittle way. And you have already got it wrong. ReactChildren
is not a type of array of react children. That's a type of React.Children
interface combining utility functions to work with children
prop.
For typing children
prop react
exposes special helper type React.PropsWithChildren
. And if you wish you may rewrite your code in more stable way:
import * as React from 'react'
import styled from 'styled-components'
interface LinkProps {
to: string;
target?: string;
as?: keyof JSX.IntrinsicElements;
className?: string;
}
const Button = styled(RLink)`
text-decoration: none;
`;
export const Link = ({
to,
target,
children,
as = 'a',
className
}: React.PropsWithChildren<LinkProps>) => {
return (
<Button
as={as}
to={to}
target={target}
rel="noopener"
className={className}
>
{children}
</Button>
);
};