Skip to content

[styled-system/components - react] Overwriting HTMLAttributes w/ own type def #29629

@nicholaai

Description

@nicholaai

@types/react: "^16.4.9"
@types/styled-system: "^3.0.1",
@types/styled-components: "^4.0.0"

I'm utilizing typescript, react, styled-components and styled-system to build out a design system. I cannot write my own type definitions that will overwrite types in HTMLAttributes interfaces, specifically interface ImgHTMLAttributes

Specifically, I'd like to pass an array to width on an image, as performed in styled-system, but the width property on ImgHTMLAttributesonly allows for a single string | number.

If the html element doesn't have a property defined on it's base interface, then I'm successfully able to define and use the property as I'd like.

For example, I've created a <Box /> component, which is a div. On it's interface, I can define width to accept whatever I choose, and successfully use width={[ 1, 1/2 ]}.

Here's the code.

This is my base box. Note that I'm importing the WidthProps interface from styled-system

import { width, WidthProps, (...more) } from 'styled-system';
import styled, { css } from '../styled-components';
import { IThemeInterface } from '../theme';

export interface IBox extends SpaceProps, WidthProps (...more) {
    /** Emergency Hatch - One-off CSS Rule Object */
    css?: any;
    /** Set of rules that define rules / styles */
    theme?: IThemeInterface;
}

export const baseRules = css`
    ${width}
    (...more)
`;

export const Box = styled.div<IBox>`
    ${() => baseRules};
`;

Box.displayName = 'Box';

export default Box;

Within @types/styled-system, the WidthProps are defined as:

export interface WidthProps {
    width?: ResponsiveSpaceValue;
}

I can use this box just fine like so

<Box width={[1 / 2, 1]}>
    Hello
</Box>

and here is my Image

import { borderRadius, BorderRadiusProps } from 'styled-system';
import { baseRules, IBox } from '../Box/Box';
import styled from '../styled-components';

interface IImage extends IBox, BorderRadiusProps {}

export const Image = styled.img<IImage>`
    ${() => baseRules};
    ${borderRadius};

    max-width: 100%;
    height: auto;
`;

Image.displayName = 'Image';

export default Image;

When hovering over Image, you get

const Image: StyledComponentClass<ClassAttributes<HTMLImageElement> & ImgHTMLAttributes<HTMLImageElement> & IImage, IThemeInterface, ClassAttributes<HTMLImageElement> & ImgHTMLAttributes<HTMLImageElement> & IImage>

When trying to use <Image /> like so

<Image width={[1 / 2, 1 / 3]} alt="Tesla Roadster" src={roadster} />

I get the error

Type 'number[]' is not assignable to type 'string | number | (string & (string | number | null)[]) | (number & (string | number | null)[]) | undefined'.
  Type 'number[]' is not assignable to type 'number & (string | number | null)[]'.
    Type 'number[]' is not assignable to type 'number'.

The expected type comes from property 'width' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<ThemedOuterStyledProps<ClassAttributes<HTMLImageElement> & ImgHTMLAttributes<HTMLImageElement> & IImage, IThemeInterface>, ComponentState, any>> & Readonly<...> & Readonly<...>'

When following the error message, I get linked to interface ImgHTMLAttributes within @types/react which has it has a own width definition:

interface ImgHTMLAttributes<T> extends HTMLAttributes<T> {
        alt?: string;
        crossOrigin?: "anonymous" | "use-credentials" | "";
        height?: number | string;
        sizes?: string;
        src?: string;
        srcSet?: string;
        useMap?: string;
        width?: number | string;
    }

I tried adding an additional width type to my interface to overwrite the HTML and it didn't work. The only time there was any affect was if i placed type any on width like so (but this kills typing):

interface IImage extends IBox, BorderRadiusProps {
    width?: any;
}

I also tried to see if i could edit ImgHTMLAttributes by doing the following:

declare module 'react' {
    interface ImgHTMLAttributes<T> {
        width?: any;
    }
}

But I get an error

"message": "Subsequent property declarations must have the same type.  Property 'width' must be of type 'string | number | undefined', but here has type 'any'.",

I also came across similar issues #22933 and #14934, but not sure how to make appropriate changes.

How would I be able to get this to work? Is what I want to do possible? Thanks in advance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions