样式化组件,定义为defaultProps的必需道具显示为丢失

时间:2019-02-27 02:19:40

标签: reactjs typescript typescript-typings styled-components definitelytyped

使用该组件时,定义为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 :(得分:2)

编辑虽然我个人对此行为很满意,但这与正常的React组件(typescript supports defaultProps)的行为不一致。

我四处张望,似乎此问题已在@types/styled-components中解决,但随后在removed due to it introducing another bug中解决了。

类型维护者的注意事项:

  

但是,TS不会设置新设置的defaultProps(又名expando)的类型,因此不要指望它会跳过必需的道具。   为了修改类型,在测试中,我添加了一个简单的帮助器函数,该函数设置defaultProps并返回修改后的类型。这仅是示例。

这里是example:

// 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>;
}

此帮助程序确实可以解决问题(并且类似于@TitianCernicova-Dragomir's approach,因此,如果您接受此正确的解决方法,则应考虑将其答案标记为正确)。

/* 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在过去确实可行-如果您有兴趣保持这种行为,我认为恢复为旧版本的@ 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

答案 1 :(得分:2)

问题在于分配defaultProps不会更改其类型,而其类型仍为默认的Partial<StyledComponentProps<"button", any, ButtonProps, never>>。您可以显式地键入const,但是键入很多类型从来都不是一件好事。一种更简单的方法是使用另一种HOC(可以用另一种HOC来解决所有问题),该HOC会改变defaultProps的类型以包含您实际在组件上设置的默认props:

// 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

答案 2 :(得分:1)

应用功能道具的一种更可靠的方法是使用功能组合。

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断言

答案 3 :(得分:0)

一种更简单更好的解决方案是在销毁时在渲染(或使用它们的位置)中指定默认值,我经常这样做,这是一个示例:

// 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
}

答案 4 :(得分:0)

您可以只创建一个常规组件,然后将该组件的引用传递给style并像设置标签一样设置样式

const PlainButton = props: ButtonProps => (/* your jsx here */);
const Button = styled(PlainButton)`
  color: #A5FFA5;
`;