Styled components + typescript: "as" is not assignable to type IntrinsicAttributes - javascript

I have a monorepo that contains a design-system made with styled components. In this design system I have a Heading component that takes a 'level' prop to adjust the CSS of the heading.
Heading
export interface HeadingProps extends HTMLAttributes<HTMLHeadingElement> {
level: 'colossus' | 'uber' | 'hero' | '1' | '2' | '3' | '4' | '5'
}
export const Heading: React.FC<HeadingProps> = ({ level = '1', children, ...rest }) => {
return (
<HeadingStyled level={level} {...rest}>
{children}
</HeadingStyled>
)
}
Usage
To use this Heading component I simply pass a level to it for the styling and the as prop to adjust what HTML is rendered.
<Heading as="h2" level="2">
Header 2
</Heading>
Problem
When I use this component I get a typescript error on the as prop
Type '{ children: string; as: string; level: "2"; }' is not assignable to type 'IntrinsicAttributes & HeadingProps & { children?: ReactNode; }'.
Property 'as' does not exist on type 'IntrinsicAttributes & HeadingProps & { children?: ReactNode; }'.
I have tried:
export interface HeadingProps extends HTMLAttributes<HTMLHeadingElement> {
level: 'colossus' | 'uber' | 'hero' | '1' | '2' | '3' | '4' | '5'
as?: React.Element | JSX.Element | JSX.IntrinsicElements
}

You're close! JSX.IntrinsicElements is an interface whose keys are the labels of the HTML tags. It itself is not a union of all HTML tags.
That means that all you need to do is
interface HeadingProps extends HTMLAttributes<HTMLHeadingElement> {
// ...
as?: keyof JSX.IntrinsicElements // Note the keyof!
}
Now as's signature is shown by TS as:
(JSX attribute) HeadingProps.as?: "symbol" | "object" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | "canvas" | ... 156 more ... | undefined
Which means your code will now work exactly as expected
<Heading as="h2" level="2"> // No TS errors! ✅
Header 2
</Heading>
TS playground link

in completion to #Aron's answer , this code working for me (styled-component + styled-system)
{ as?: ComponentType<any> | keyof JSX.IntrinsicElements | string | undefined }

Related

Dynamically import one Icon using icon-name from react-icons in nextjs/typescript

I'm trying to dynamically import an icon from react-icons following the this Nextjs instructions but I get an error.
This is my code:
import React, { Suspense } from 'react';
import dynamic from 'next/dynamic';
interface IconProps {
name: string;
}
function Icon({ name }: IconProps) {
const MyIcon = dynamic(() => import('react-icons/fi/index.js').then((mod) => mod[name]), {suspense: true});
return (
<Suspense fallback={`Loading...`}>
<MyIcon />
</Suspense>
);
}
export default Icon
This is the error:
Argument of type '() => Promise<IconType | React.ComponentClass<never, any> | React.FunctionComponent<never> | { default: React.ComponentType<never>; }>' is not assignable to parameter of type 'DynamicOptions<{}> | Loader<{}>'.
Type '() => Promise<IconType | React.ComponentClass<never, any> | React.FunctionComponent<never> | { default: React.ComponentType<never>; }>' is not assignable to type '() => LoaderComponent<{}>'.
Type 'Promise<IconType | ComponentClass<never, any> | FunctionComponent<never> | { default: ComponentType<never>; }>' is not assignable to type 'LoaderComponent<{}>'.
Type 'IconType | ComponentClass<never, any> | FunctionComponent<never> | { default: ComponentType<never>; }' is not assignable to type 'ComponentType<{}> | { default: ComponentType<{}>; }'.
Type 'ComponentClass<never, any>' is not assignable to type 'ComponentType<{}> | { default: ComponentType<{}>; }'.
Type 'ComponentClass<never, any>' is not assignable to type 'ComponentClass<{}, any>'.
Types of property 'getDerivedStateFromProps' are incompatible.
Type 'GetDerivedStateFromProps<never, any> | undefined' is not assignable to type 'GetDerivedStateFromProps<{}, any> | undefined'.
Type 'GetDerivedStateFromProps<never, any>' is not assignable to type 'GetDerivedStateFromProps<{}, any>'.
Types of parameters 'nextProps' and 'nextProps' are incompatible.
Type 'Readonly<{}>' is not assignable to type 'never'.ts(2345)
What am I doing wrong?

Material UI Custom theme color assign Typescript

I created my own custom theme and I'm trying to assing one of the colors I created to a button, but when I'm trying to set it as:
color={theme.pallete.lightGrey}
I'm getting this error:
No overload matches this call
Overload 1 of 3, '(props: { href: string; } & { children?: ReactNode; classes?: Partial<ButtonClasses> | undefined; color?: "inherit" | "primary" | "secondary" | "success" | "error" | "info" | "warning" | undefined; ... 9 more ...; variant?: "text" | ... 2 more ... | undefined; } & Pick<...> & CommonProps & Pick<...>): Element', gave the following error.
Does this means that you can't assign custom colors to the color attribute? Only through styling
If you are using typescript use the global theme approach
in this case,
I have 2 custom colours which are neutral and backgroundSecondary so you need to register the types in material UI types
declare module "#mui/material/styles" {
interface CustomPalette {
neutral: {
main: string;
contrastText: string;
};
backgroundSecondary: string;
}
interface Palette extends CustomPalette {}
interface PaletteOptions extends CustomPalette {}
}
Then register that button show be able to read the custom colors
In my case I only need neutral to be read by the Button component so you do this
declare module "#mui/material/Button" {
interface ButtonPropsColorOverrides {
neutral: true;
}
}
You set up your theme (in most cases this would have already been setup)
let theme = createTheme({
palette: {
primary: {
main: "some-color",
},
secondary: {
main: "some-color",
},
error: {
main: "some-color",
},
neutral: {
main: "#fff",
contrastText: "#002255",
},
backgroundSecondary: "#f8fafc",
},
});
Finally the usage
<Button variant="outlined" color="neutral"> Click Me</Button>

`No overload matches this call` in recharts

I'm building a chart in react using recharts-- I am using an example from their docs here: https://codesandbox.io/s/zen-ellis-30cdb?file=/src/App.tsx
In codesandbox, the project compiles and runs, but you can see the error hint in the code at line 53 (and it generates a lot of warnings in the console).
On my local project it fails to compile and prints the error to the screen. What is causing this, and can it be fixed?
Overload 1 of 2, '(props: Readonly<Props>): Line', gave the following error.
Type '{ dataKey: string; data: { category: string; value: number; }[]; name: string; key: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Line> & Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "string" | ... 474 more ... | "animationId"> & Partial<...> & Partial<...>'.
Property 'data' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Line> & Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "string" | ... 474 more ... | "animationId"> & Partial<...> & Partial<...>'.
Overload 2 of 2, '(props: Props, context?: any): Line', gave the following error.
Type '{ dataKey: string; data: { category: string; value: number; }[]; name: string; key: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Line> & Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "string" | ... 474 more ... | "animationId"> & Partial<...> & Partial<...>'.
Property 'data' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Line> & Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "string" | ... 474 more ... | "animationId"> & Partial<...> & Partial<...>'. TS2769
130 | {/*{buildSeries()}*/}
131 | {series.map(s => (
> 132 | <Line dataKey="value" data={s.data} name={s.name} key={s.name} />
| ^
133 | ))}
134 | </LineChart>
135 | }```
The prop data does not exist in the Line chart, you should pass it in the AreaChart wrapper

React: How to pass a function ("interpolations") to a styled component's template

I'm trying to build a bar graph using react. I'm using props to try to pass a function ("interpolations") to a styled component's template in styled component but its showing an error message:
my styled-component:
export const Bar = styled.div`
border-radius: 3px;
height: 55px;
background-color: #64b2d1;
width: ${props => props.width};
animation-name: ${showBarOne};
animation-duration: 1.2s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
`;
my component:
const Volume = ({widthBar}) =>{
return(
<VolumeContainer>
<ProgressContainer>
<Bar width='{widthBar}' ></Bar>
</ProgressContainer>
</VolumeContainer>
)
}
its showing a error inside the Volume component on the width variable.
No overload matches this call.
Overload 1 of 2, '(props: Pick<Pick<Pick<DetailedHTMLProps<HTMLAttributes, HTMLDivElement>, "slot" | "style" | ... 252 more ... | "onTransitionEndCapture"> & { ...; }, "slot" | ... 254 more ... | "onTransitionEndCapture"> & Partial<...>, "slot" | ... 254 more ... | "onTransitionEndCapture"> & { ...; } & { ...; }): ReactElement<...>', gave the following error.
Type '{ width: string; }' is not assignable to type 'IntrinsicAttributes & Pick<Pick<Pick<DetailedHTMLProps<HTMLAttributes, HTMLDivElement>, "slot" | ... 253 more ... | "onTransitionEndCapture"> & { ...; }, "slot" | ... 254 more ... | "onTransitionEndCapture"> & Partial<...>, "slot" | ... 254 more ... | "onTransitionEndCapture"> & { ...; } & { ...; }'.

TSX: Props type definition for a number input

In JSX/TSX syntax from react, all inputs seems to be define with the same props declaration which is InputHTMLAttributes:
interface InputHTMLAttributes<T> extends HTMLAttributes<T> {
accept?: string;
alt?: string;
autoComplete?: string;
autoFocus?: boolean;
capture?: boolean | string; // https://www.w3.org/TR/html-media-capture/#the-capture-attribute
checked?: boolean;
crossOrigin?: string;
disabled?: boolean;
form?: string;
formAction?: string;
formEncType?: string;
formMethod?: string;
formNoValidate?: boolean;
formTarget?: string;
height?: number | string;
list?: string;
max?: number | string;
maxLength?: number;
min?: number | string;
minLength?: number;
multiple?: boolean;
name?: string;
pattern?: string;
placeholder?: string;
readOnly?: boolean;
required?: boolean;
size?: number;
src?: string;
step?: number | string;
type?: string;
value?: string | ReadonlyArray<string> | number;
width?: number | string;
onChange?: ChangeEventHandler<T>;
}
I want to define a specific type for only an input of type number so that TypeScript complains if I use a checked or disabled prop on it:
<!-- invalid `checked` prop -->
<input type="number" step="any" min="0" max="100" value="22.33" checked={true} />
So is there already a type definition for that coming from React?
I know that I can just define it like the following but I would prefer to use an official source:
interface InputNumberProps {
type: 'number';
max?: number | string;
min?: number | string;
...
}
I think the simplest solution would be to wrap input into your own component and provide custom type definitions. As far as I know, there is nothing like that coming from official React types. This is done because HTML itself is not strongly typed and it is valid to do something like <input type="number" checked />
As for type definition something like that would suffice:
interface NumberInputProps {
type: "number"
value: number
min?: number
max?: number
}
interface TextInputProps {
type?: "text"
value: string
}
interface CheckboxInputProps {
type: "checkbox"
checked?: boolean
}
type InputProps = NumberInputProps | TextInputProps | CheckboxInputProps
const Input: React.FC<InputProps> = (props) => <input {...props} />
That is a lot of typings for sure (and this does not event include things like events, class names etc). Also, this requires even more JS code if you want to provide stronger types for something like number fields (converting strings into numbers etc.)
If you want to modify the original InputHTMLAttributes (which is a better solution than writing your own types from scratch) interface, overriding and/or omitting some of the fields there are helper types like Omit that would allow you to do so.
type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "checked" | "min" | "max"> & (NumberInputProps | TextInputProps | CheckboxInputProps)
You can also split different input types into different components if you find writing too much logic in one component. Do whatever suits you
P.S. on the second note. This sounds like type over-engeneering
You can use and extends the official types:
export type InputProps = React.DetailedHTMLProps<
React.InputHTMLAttributes<HTMLInputElement>,
HTMLInputElement
>;
interface NumberInputProps extends Omit<InputProps, 'checked' | 'disabled'> {
value: number
}
Using this code you exclude checked and disabled properties and overrides value to allow only numbers instead string | ReadonlyArray | number

Categories

Resources