Сложный объект в Typescript надо, чтобы classname был или строкой, или объектом

Я хочу дать возможность передавать или строку, или объект с полями типо вот так

className={{
    tag: 'tag-class',
    tooltip: {
        base: 'tooltip-base-class',
        content: 'tooltip-content-class'
    },
}}

Внутри компонента я пытаюсь принять

interface TooltipProps {
    className?: string | ClassNameObject;
}

interface ClassNameTooltip {
    base?: string;
    content?: string;
}

interface ClassNameObject {
    tag?: string;
    tooltip?: ClassNameTooltip;
}

Казалось что все окей, но получаю ошибку

Тип "{ tag: string; tooltip: { base: string; content: string; }; }" не может быть назначен для типа "string | (ClassNameObject & string)".
Тип "{ tag: string; tooltip: { base: string; content: string; }; }" не может быть назначен для типа "ClassNameObject & string". Тип "{ tag: string; tooltip: { base: string; content: string; }; }" не может быть назначен для типа "string"

Прошу помочи в решение такой задачи


Ответы (1 шт):

Автор решения: Edvard

Ответ оказался прост, надо было начать копать из-начала, в общем для создания объекта я использовал Proxy чтобы можно было указать какой хочешь блок примерно как в frame motion где указывается префикс и через точку нужный блок motion.div.

А чтобы получить все свойства для указанного тэга, я их собирал как бы назад, в этом и был конфликт, что она ожидала для Proxy стандартный className: string а еще сверху пытался добавить свой className

Решение

В типе для создания Proxy нужно удалить className и после чего добавить свой.

type TooltipComponent = <T extends keyof JSX.IntrinsicElements>(
    props: TooltipProps & Omit<JSX.IntrinsicElements[T], 'className'>
) => ReactElement | null;

После спокойно создаем новый элемент:

interface TooltipProps {
    className?: string | ClassNameObject;
}

const createTooltipComponent = (Tag: ComponentType<any> | keyof JSX.IntrinsicElements): TooltipComponent => {
    return ({
        className,
    }: TooltipProps): JSX.Element => {
       }
    )

Таким образом мы получаем именно наш className, который можем описать как хотим.

Полный код:

interface ClassNameTooltip {
    base?: string;
    content?: string;
}

interface ClassNameObject {
    tag?: string;
    tooltip?: ClassNameTooltip;
}

interface TooltipProps {
    className?: string | ClassNameObject;
}

type TooltipComponent = <T extends keyof JSX.IntrinsicElements>(
    props: TooltipProps & Omit<JSX.IntrinsicElements[T], 'className'>
) => ReactElement | null;

const createTooltipComponent = (Tag: ComponentType<any> | keyof JSX.IntrinsicElements): TooltipComponent => {
    return ({
        className,
    }: TooltipProps): JSX.Element => {
        return (
            <Tag>
            /* ... */
            </Tag>
        );
    };
};

const tooltip: { [K in keyof JSX.IntrinsicElements]: TooltipComponent } = new Proxy({}, {
    get: (_, prop: string) => createTooltipComponent(prop as keyof JSX.IntrinsicElements),
}) as { [K in keyof JSX.IntrinsicElements]: TooltipComponent };

export default tooltip;
→ Ссылка