简体   繁体   中英

How to write generic typings for HOC with typescript?

I am writing the typings for an existing UI library which has a Base component which takes in Element as a string and does <Element {...props} What is the correct way to type it?

I have tried writing a generic type with

type AllTypes = HTMLProps<HTMLDivElement> | HTMLProps<HTMLInputElement> // ... And other HTML element types
type BaseTypes <T extends AllTypes> = {
    Element: string;
    props: T;
    className?: string;
    baseClassName?: string;
}


export const BaseComponent = <T extends AllTypes>({
    Element = "div",
    baseClassName,
    className,
    ...props
}: BaseTypes<T> ) => {
    const classNames = [];

    if (baseClassName) {
        classNames.push(baseClassName);
    }
    if (className) {
        classNames.push(className);
    }



    return (
        <Element 
            className={classNames.join(' ')}
            {...props }
        />
    )
}

and on the other side

type newType = {
    Element: string;
    props: HTMLProps<HTMLInputElement>,
    className?: string;
    baseClassName?:string;
}

const InputField = (props: newType): ReactElement => {
    return (
        <BaseComponent<HTMLProps<HTMLInputElement>>
            Element="input"
            baseClassName="ff-input"
            {...props}
        />
    );
};

export default InputField;

I am getting errors like cannot assign {className:string} to IntrinsicAttributes.

I would like to know the correct way to type the BaseComponent such that the props types are decided based on the Element value. Any suggestion would be appreciated.

You can't use just a plain string as Element with JSX. If you want to use a string you could swap to doing this...

React.createElement(Element, { ...props, className: classNames.join(' ') }, null);

Alternatively you can amend the type of Element to be ElementType<any> , for example...

import * as React from 'react';
import { HTMLProps, ReactElement, ElementType } from 'react';

export type BaseProps = {
    Element: ElementType<any>;
    className?: string;
    baseClassName?: string;
}

export const BaseComponent = <T extends {}>({
    Element = "div",
    baseClassName,
    className,
    ...props
}: T & BaseProps) => {
    const classNames = [];

    if (baseClassName) {
        classNames.push(baseClassName);
    }
    if (className) {
        classNames.push(className);
    }

    return <Element { ...{...props, className: classNames.join(' ') } } />;
}

const InputField = (props: HTMLProps<HTMLInputElement>): ReactElement => {
    return (
        <BaseComponent<HTMLProps<HTMLInputElement>>
            Element="input"
            baseClassName="ff-input"
            {...props}
        />
    );
};

export default InputField;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM