Вопрос или проблема
Я создал базовый компонент иконки, который расширяет SVG
из react-inlinesvg, используя styled-components
, чтобы позволить кастомизировать стили. Кроме того, вызов attr
указывает свойство src
для компонента SVG
, поскольку разные файлы SVG
будут иметь разные реализации стилей, поэтому каждый из них является собственным компонентом. Вот соответствующий код:
import SVG from 'react-inlinesvg';
import { CSSProperties as _CSSProperties } from "react";
import styled, { Interpolation } from "styled-components";
// Удаляем undefined из CSSProperties, так как все эти константы гарантированно
// существуют.
export type RequiredCSSProperties = Required<_CSSProperties>;
// Добавляем проп, который позволяет добавить пользовательский CSS к экземпляру стилизованного компонента без
// создания нового стилизованного компонента.
export type WithCSSProp<T={}> = T & { $css?: Interpolation<Omit<T, "$css">> };
export type MenuIconStyleAttributes = {
$color?: RequiredCSSProperties['stroke'] | null;
$width?: RequiredCSSProperties['width'];
$height?: RequiredCSSProperties['height'];
};
export default styled(SVG).attrs<WithCSSProp<MenuIconStyleAttributes>>(
(props) => ({
src: '/icons/menu.svg',
$color: props.$color ?? null,
$width: props.$width ?? '24px',
$height: props.$height ?? 'auto',
$css: props.$css ?? null
})
)`
width: ${({$width}) => $width};
height: ${({$height}) => $height};
> path {
stroke: ${({$color, theme}) => $color ?? theme.textColor};
}
${({$css}) => $css}
`;
Моя проблема заключается в том, что, когда я использую этот компонент, свойство src
рассматривается как обязательное, хотя оно указано со статическим значением. Точная ошибка:
Нет совпадений для этого вызова.
Перегрузка 1 из 2, '(props: PolymorphicComponentProps<"web", FastOmit<Substitute<Substitute<{ string?: string | number | undefined; stroke?: string | undefined; clipPath?: string | undefined; color?: string | undefined; cursor?: string | number | undefined; ... 481 более ...; uniquifyIDs?: boolean | undefined; }, { ...; }>, WithCSSProp<...>>, never>, void, void, {}, {}>): Element', вызвала следующую ошибку.
Свойство 'src' отсутствует в типе '{}' но обязательно в типе 'FastOmit<Substitute<FastOmit<Substitute<Substitute<{ string?: string | number | undefined; stroke?: string | undefined; clipPath?: string | undefined; color?: string | undefined; cursor?: string | number | undefined; direction?: string | ... 1 более ... | undefined; ... 480 более ...; uniquifyIDs?: boolean | undefined...'.
Перегрузка 2 из 2, '(props: FastOmit<Substitute<Substitute<{ string?: string | number | undefined; stroke?: string | undefined; clipPath?: string | undefined; color?: string | undefined; cursor?: string | number | undefined; direction?: string | ... 1 более ... | undefined; ... 480 более ...; uniquifyIDs?: boolean | undefined; }, { ...; }>, WithCSSProp<...>>, never>): ReactNode', вызвала следующую ошибку.
Свойство 'src' отсутствует в типе '{}' но обязательно в типе 'FastOmit<Substitute<Substitute<{ string?: string | number | undefined; stroke?: string | undefined; clipPath?: string | undefined; color?: string | undefined; cursor?: string | number | undefined; direction?: string | ... 1 более ... | undefined; ... 480 более ...; uniquifyIDs?: boolean | undefined; }, { ...; }>, With...'.
Я могу переопределить src
, чтобы сделать его необязательным, но значение все равно будет проигнорировано, поэтому я действительно просто хочу избавиться от этого пропа полностью. Это возможно?
Ответ или решение
Проблема с обязательным свойством src
в компоненте, использующем styled-components
Ваша проблема с тем, что свойство src
компонента SVG
из библиотеки react-inlinesvg
воспринимается как обязательное, хотя значение задается статически, действительно может вызывать затруднения. Расберем эту ситуацию более подробно.
Понимание ошибки
Ошибка, с которой вы столкнулись, указывает на то, что TypeScript требует наличие свойства src
для вашего компонента, несмотря на то, что вы задали его статически через attrs
. Это происходит потому, что типы, связанные с SVG
, не учитывают статическую привязку, делая src
обязательным свойством.
Как это исправить
Варианты решения проблемы зависят от вашего подхода к типизации. Рассмотрим несколько стратегий:
-
Создание обертки: Один из способов избавиться от обязательного свойства
src
— это создать новый компонент (обертку) вокруг компонентаSVG
, который не будет требоватьsrc
. Вы можете создать компонент, который добавляетattrs
и передает необходимые пропсы без необходимости явно указыватьsrc
:import SVG from 'react-inlinesvg'; import { styled } from 'styled-components'; const StyledSVG = styled(SVG).attrs({ src: '/icons/menu.svg', })` width: ${({ $width }) => $width}; height: ${({ $height }) => $height}; > path { stroke: ${({ $color, theme }) => $color ?? theme.textColor}; } ${({ $css }) => $css} `; const MenuIcon = (props) => <StyledSVG {...props} />;
В этом случае ваш новый компонент
MenuIcon
больше не требует, чтобыsrc
был предоставлен как пропс. -
Переопределение типов: Вы также можете расширить типы, чтобы сделать свойство
src
не обязательным в пользовательском интерфейсе, но оставить его обязательным в реализациях. Здесь вам нужно переопределить интерфейс, который используется вprops
:type CustomSVGProps = Omit<OriginalSVGProps, 'src'>; const StyledSVG = styled(SVG).attrs<CustomSVGProps>(props => ({ src: '/icons/menu.svg', ...props, }))` /* ваши стили */ `;
-
Использование
defaultProps
: Альтернативно, вы могли бы установитьsrc
какdefaultProps
:StyledSVG.defaultProps = { src: '/icons/menu.svg', };
В этом случае, если
src
не будет передан, компонент будет использовать заданное по умолчанию значение.
Заключение
Для решения проблемы с обязательным свойством src
в вашем компоненте SVG
из react-inlinesvg
, важно переосмыслить подход к типизации, установлению пропсов или созданию обертки. Независимо от выбранного вами метода, этот подход поможет вам избавиться от ошибок компиляции и упростить использование вашего компонента.
Если у вас возникли дополнительные вопросы по этой теме или есть сложности с реализацией, не стесняйтесь задавать их. Я готов помочь вам разобраться!