You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The TypographyProps definition is causing type extension issues due to its implementation as a type union of HTML node types, specifically HTMLElement and HTMLAnchorElement. This use of a type union introduces inconsistency at the type level, which is particularly evident when attempting to extend TypographyProps with an interface.
Attempted interface extension results in the TypeScript error:
An interface can only extend an object type or intersection of object types with statically known members.ts(2312)
I understand the goal, which is to achieve type safety for attributes such as href or rel related to HTMLAnchorElement. But there is a mix between type definitions and the usage of styled-components as property. This application creates a dichotomy at the type level that's not visually apparent, and it intermingles capabilities of styled-components without aligning them properly with the type definitions.
EDS should allow extensions no matter if a user decides to use type or interface, it should not be forced to use one or another.
As EDS is using styled-components, which allows to change dynamically the component HTML node element, it should also offers typesafety around that node. I should not have all the attributes no matter the html node i decide to use as that will bring a lot of confusion.
Steps to reproduce the bug
importtype{TypographyProps}from'@equinor/eds-core-react';interfaceTypography2PropsextendsTypographyProps{/** New props ... */}
Expected behavior
I should at least be able to extend the TypographyProps using interface.
Ideal solution, if i use as="a" then i will have autocomplete for the properties that are for an anchor element.
Possible solutions
An easy fix to allow extensions of the TypographyProps unsing interfaces is to instead of using type change it to interface which will force you to have double extension instead of a type union:
exportinterfaceTypographyPropsextendsextendsHTMLAttributes<HTMLElement>,AnchorHTMLAttributes<HTMLAnchorElement>{/** Typography variants, specifies which variant to use. */variant?: TypographyVariants/** Typography groups, specifies which group to use. */group?: TypographyGroups/** Bold text. */bold?: boolean/** Italic text. */italic?: boolean/** Link. */link?: boolean/** Typography colors. */// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituentscolor?: ColorVariants|string/** Token. */token?: Partial<TypographyType>/** Number of lines. */lines?: number}
Unsure if that change will produce other type errors.
A "more" correct fix as it will add inference arround the render engine (styled-components) is to allow autocomplete based on the as propert. A way to achive it is to create a wrapper around the forwardRef which will infer the types based on the as property.
A possible implementation, is just an example I think is quite correct but im using some deprecated types, but to test the idea should be good (Also keep in mind react 19 will remove the need for forwardRef, which then this code will need some refactor):
import{forwardRefasforwardReactRef,typeComponentProps,typeComponentPropsWithoutRef,typeElementType,typeExoticComponent,typeForwardRefRenderFunction,typePropsWithoutRef,typeValidationMap,typeWeakValidationMap,}from"react";typeAs=ElementType;typePropsOf<TextendsAs>=ComponentPropsWithoutRef<T>;typeRightJoinProps<SourcePropsextendsobject={},OverridePropsextendsobject={},>=OmitCommonProps<SourceProps,keyofOverrideProps>&OverrideProps;typeOmitCommonProps<Target,OmitAdditionalPropsextendskeyofany=never,>=Omit<Target,"transition"|"as"|"color"|"translate"|OmitAdditionalProps>&{htmlTranslate?: "yes"|"no"|undefined;};// ! If you plan to use this implementation keep in mind that this type must be called "ForwardRefExoticComponent" or storybook will not autogenerate the argTypestypeForwardRefExoticComponent<ETextendsAs,Pextendsobject={},>=ExoticComponent<P>&{<AsComponentextendsAs=ET>(props: MergeWithAsProp<ComponentProps<ET>,ComponentProps<AsComponent>,P,AsComponent>): JSX.Element;displayName?: string;defaultProps?: Partial<P>;propTypes?: WeakValidationMap<P>;contextTypes?: ValidationMap<any>;id?: string;};typeMergeWithAsProp<CPextendsobject,AsPropsextendsobject,AdditionalPropsextendsobject={},AsComponentextendsAs=As,>=(|RightJoinProps<CP,AdditionalProps>|RightJoinProps<AsProps,AdditionalProps>)&{as?: AsComponent;};/** * Creates a forward ref for a React functional component that adds the "as" property. * * @typeParam P - The Component props type. * @typeParam ET - The React.ElementType. * * @param component - The functional component. * @returns Forwarded React component. */exportfunctionforwardRefWithAs<Pextendsobject,ETextendsAs>(component: ForwardRefRenderFunction<any,RightJoinProps<PropsOf<ET>,P>&{as?: As;}>){returnforwardReactRef(component)asForwardRefExoticComponent<ET,PropsWithoutRef<P>>;}
Just an update for the easy solution, as what i wrote it was just the general idea but in terms of code if you copy paste will not work. Is not possible to extend from both HTMLAttributes<HTMLElement> and AnchorHTMLAttributes<HTMLAnchorElement> at the same time as there is properties with the same name but not identically on the type def.
A solution for that could be:
exportinterfaceTypographyPropsextendsHTMLAttributes<HTMLElement>,Omit<AnchorHTMLAttributes<HTMLAnchorElement>,keyofHTMLAttributes<HTMLElement>>{/** Typography variants, specifies which variant to use. */variant?: TypographyVariants;/** Typography groups, specifies which group to use. */group?: TypographyGroups;/** Bold text. */bold?: boolean;/** Italic text. */italic?: boolean;/** Link. */link?: boolean;/** Typography colors. */color?: ColorVariants|string;/** Token. */token?: Partial<TypographyType>;/** Number of lines. */lines?: number;}
I'm omiting the props from AnchorHTMLAttributes<HTMLAnchorElement> that their key is already on HTMLAttributes<HTMLElement> because your forwardRef is based on HTMLElement but you will still have autocomplete for props like rel or href.
Describe the bug
The
TypographyProps
definition is causing type extension issues due to its implementation as a type union of HTML node types, specificallyHTMLElement
andHTMLAnchorElement
. This use of a type union introduces inconsistency at the type level, which is particularly evident when attempting to extendTypographyProps
with an interface.Attempted interface extension results in the TypeScript error:
I understand the goal, which is to achieve type safety for attributes such as
href
orrel
related toHTMLAnchorElement
. But there is a mix between type definitions and the usage of styled-componentsas
property. This application creates a dichotomy at the type level that's not visually apparent, and it intermingles capabilities of styled-components without aligning them properly with the type definitions.type
orinterface
, it should not be forced to use one or another.styled-components
, which allows to change dynamically the component HTML node element, it should also offers typesafety around that node. I should not have all the attributes no matter the html node i decide to use as that will bring a lot of confusion.Steps to reproduce the bug
Expected behavior
I should at least be able to extend the
TypographyProps
using interface.Ideal solution, if i use
as="a"
then i will have autocomplete for the properties that are for an anchor element.Possible solutions
An easy fix to allow extensions of the
TypographyProps
unsing interfaces is to instead of using type change it to interface which will force you to have double extension instead of a type union:A "more" correct fix as it will add inference arround the render engine (styled-components) is to allow autocomplete based on the
as
propert. A way to achive it is to create a wrapper around theforwardRef
which will infer the types based on theas
property.A possible implementation, is just an example I think is quite correct but im using some deprecated types, but to test the idea should be good (Also keep in mind react 19 will remove the need for forwardRef, which then this code will need some refactor):
Usage:
Now You should have autocomplete for
href
only if you useas="a"
The text was updated successfully, but these errors were encountered: