Стилизованные компоненты, необходимые реквизиты, определенные как defaultProps, отображаются как отсутствующие - PullRequest
0 голосов
/ 27 февраля 2019

Обязательные реквизиты, определенные как defaultProps, отображаются как отсутствующие при использовании компонента.

// Button.ts
interface ButtonProps {
  size: 'small' | 'medium' | 'large';
  inverted: boolean;
  raised: boolean;
}

const Button = styled('button')<ButtonProps>`...`

Button.defaultProps = {
  size: 'medium',
  inverted: false,
  raised: false,
}

export default Button
// Hello.tsx
import Button from './Button'

const Hello = () => <Button>Hello</Button>

Кнопка подсвечивается красным и отображает это сообщение об ошибке:

Type '{}' is missing the following properties from type 
'Pick<Pick<Pick<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement>, "form" | "style" | "title" | "key" | "autoFocus" |
"disabled" | "formAction" | ... 255 more ... | "onTransitionEndCapture"> &
{ ...; } & ButtonProps, "form" | ... 329 more ... | "raised"> & 
Partial<...>, "form" | ... 329 ...': size, inverted, and 1 more.

Версии:

"@types/styled-components": "^4.1.9",
"@types/react": "^16.8.5",
"@types/react-dom": "^16.8.2",

"typescript": "^3.3.3333",
"styled-components": "^4.1.3"
"react": "^16.8.3",
"react-dom": "^16.8.3",

Ответы [ 5 ]

0 голосов
/ 06 марта 2019

Вы можете просто создать обычный компонент, затем передать ссылку на этот компонент в style и установить стили так же, как при использовании тега

const PlainButton = props: ButtonProps => (/* your jsx here */);
const Button = styled(PlainButton)`
  color: #A5FFA5;
`;
0 голосов
/ 05 марта 2019

Более простым и лучшим решением было бы назначить значения по умолчанию для рендера (или где вы их используете) во время деструктурирования, я делаю это часто, вот пример:

// Button.ts
interface ButtonProps {
    size?: 'small' | 'medium' | 'large'; // making optional via '?' sign
    inverted?: boolean;
    raised?: boolean;
}

render() {
    const { size = 'medium', inverted = false, raised = false } = this.props;

    // Use these props, if none are passed by user, these defaults will be used 
    // otherwise user versions will be used
}

0 голосов
/ 02 марта 2019

Проблема в том, что присвоение defaultProps не изменит его тип, который останется по умолчанию Partial<StyledComponentProps<"button", any, ButtonProps, never>>.Вы можете явно ввести const, но набирать много типов никогда не бывает весело.Более простой подход заключается в использовании другого HOC (все можно решить с помощью другого HOC ?), который изменяет тип defaultProps, чтобы содержать реквизиты по умолчанию, которые вы фактически установили для компонента:

// Button.ts
interface ButtonProps {
    size: 'small' | 'medium' | 'large';
    inverted: boolean;
    raised: boolean;
}

function withDefault<T extends { defaultProps?: Partial<TDefaults> }, TDefaults>(o: T, defaultProps: TDefaults): T & { defaultProps: TDefaults } {
    o.defaultProps = defaultProps;
    return o as any;
}

const Button = withDefault(styled('button') <ButtonProps>`...`, {
    size: 'medium',
    inverted: false,
    raised: false,
})

export default Button

const Hello = () => <Button>Hello</Button> // OK now
0 голосов
/ 05 марта 2019

Более надежный способ применения реквизита по умолчанию - использование функциональной композиции.

function withDefaults<T extends React.ComponentType<any>, U extends Partial<React.ComponentProps<T> & JSX.IntrinsicAttributes>>(Component: T, defaults: U): React.FC<Omit<React.ComponentProps<T>, keyof U> & Partial<U>> {
  return props => React.createElement(Component, { ...defaults, ...props });
}

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

Использование:

const ButtonWithDefaults = withDefaults(Button, {
  size: 'medium',
  inverted: false,
  raised: false,
});

() => (
<ButtonWithDefaults>
  Hello
</ButtonWithDefaults>
);

Достигается все:

  • ✅ ничего не видоизменено
  • ✅ типы проверены
  • ✅ вы получаете правильное завершение кода
  • ✅ нет as any утверждение
0 голосов
/ 02 марта 2019

Редактировать Хотя я лично согласен с таким поведением, оно не согласуется с поведением обычного компонента React ( машинопись поддерживает defaultProps).

Я немного осматриваюсь, и кажется, что это было исправлено в @types/styled-components, но затем было удалено из-за появления еще одной ошибки .

Примечание от сопровождающего типа:

Тем не менее, TS не устанавливает тип вновь установленного defaultProps (также называемого expando), поэтому не ожидайте, что это позволит пропустить требуемые реквизиты.Чтобы изменить тип, в тестах я добавил простую вспомогательную функцию, которая устанавливает defaultProps и возвращает измененный тип.Это только для примера.

Вот этот пример:

// example of a simple helper that sets defaultProps and update the type
type WithDefaultProps<C, D> = C & { defaultProps: D };

function withDefaultProps<C, D>(component: C, defaultProps: D): WithDefaultProps<C, D> {
    (component as WithDefaultProps<C, D>).defaultProps = defaultProps;
    return component as WithDefaultProps<C, D>;
}

Этот помощник решает проблему (и похож на * 1026Подход * @ TitianCernicova-Dragomir , поэтому вы должны пометить его ответ как правильный, если вы примете это как правильный обходной путь).

/* setup */
interface Props {
    requiredProp: number;
}

const defaultProps: Props = {
    requiredProp: 1,
};

const Component = styled.div<Props>``;
const ComponentWithDefault = withDefaultProps(Component, defaultProps);

export { Component, ComponentWithDefault };

/* usage */
import { Component, ComponentWithDefault } from './component.tsx';

const a = <Component /> // Property requiredProp is missing...
const b = <ComponentWithDefault /> // no error

Требования:

"@types/styled-components": "^4.1.12",
"typescript": "^3.2.4",

defaultProps с styled-components работали в прошлом - если вы заинтересованы в сохранении такого поведения, я думаю, что может помочь возврат к более старой версии @ types / styled-components;Я думаю, что это 4.1.8, но я не уверен.

Оригинальный ответ


Возможно, не тот ответ, который вы ищете, но я нахожу этоповедение желательноЕсли вы уже определили определенные свойства в defaultProps, разве это не сделает эти свойства необязательными?

Я бы сделал необязательные реквизиты по умолчанию:

interface ButtonProps {
  size?: 'small' | 'medium' | 'large';
  inverted?: boolean;
  raised?: boolean;
}

Или оставил бы их нетронутыми, ноОберните Partial<>, так как вы уже предоставили значение по умолчанию для всех из них

const Button = styled('button')<Partial<ButtonProps>>`...`

Еще одна вещь, которую я обычно делаю, - это сначала определить defaultProps в переменной, чтобы я мог правильно определить ихинтерфейс:

interface Props { ... }

const defaultProps: Props = { ... }

const Component = styled.div<Props>`...`
Component.defaultProps = defaultProps
...