英文:
Comine various interfaces and props in React with typescript
问题
以下是翻译好的内容:
我现在有一个像这样的接口。这代表了“基本按钮”。
export interface ButtonProps {
    backgroundColor?: Colors,
    children?: React.ReactNode | JSX.Element,
    style?: CSSProperties,
    disabled?: boolean,
    onClick?: () => void,
}
现在我想在这个基础上构建一个“文本按钮”,一个包含文本的按钮。
interface TextButtonProps {
    buttonProps?: ButtonProps,
    textColor?: Colors,
}
现在我想从`ButtonProps`中提取`backgroundColor`属性,将其用于`<TextButton />`。
const TextButton = ({
                        backgroundColor,
                        textColor = 'black',
                        textProps,
                        ...buttonProps,
                    }: TextButtonProps) => {
    return (
        ...
    )
}
是否有一种方法可以使`backgroundColor`在`<TextButton />`中可用,而不必在`TextButtonProps`中再次明确命名它?我可以这样做:
interface TextButtonProps {
    backgroundColor?: Colors,
    buttonProps?: ButtonProps,
    textColor?: Colors,
}
但这样我会重复自己,因为`ButtonProps`已经包含了`backgroundColor`。
英文:
I am having an interface like this. This represents the "base button".
export interface ButtonProps {
    backgroundColor?: Colors,
    children?: React.ReactNode | JSX.Element,
    style?: CSSProperties,
    disabled?: boolean,
    onClick?: () => void,
}
I now want to build ontop of that a "Text Button", a Button containing text.
interface TextButtonProps {
    buttonProps?: ButtonProps,
    textColor?: Colors,
}
I knwo want to extract the property backgroundColor from ButtonProps, using it in <TextButton />
const TextButton = ({
                        backgroundColor,
                        textColor = 'black',
                        textProps,
                        ...buttonProps,
                    }: TextButtonProps) => {
    return (
        ...
    )
Is there a way to make backgroundColor available to <TextButton />, without explicitly naming it again in the TextButtonProps? I could do it like this
interface TextButtonProps {
    backgroundColor?: Colors,
    buttonProps?: ButtonProps,
    textColor?: Colors,
}
But I would repeat myself, because ButtonProps contains already backgroundColor.
答案1
得分: 1
你可以保持类型不变,并将属性传递给Button:
const TextButton: React.FC<TextButtonProps> = ({ textColor, buttonProps }) => (
  <Button {...buttonProps}>
    <Text color={textColor}>...</Text>
  </Button>
);
如果你需要在<TextButton>内部使用,例如ButtonProps.backgroundColor,那么你需要编写类似这样的代码<Text foreground={textColor} background={buttonProps?.backgroundColor}>。个人而言,我觉得TextButtonProps.buttonProps有点令人困惑:考虑一下调用者:
<TextButton textColor="red" buttonProps={{ backgroundColor: "green" }}>
  Click me
</TextButton>
真的令人困惑。
作为一个注释:你可以从类型中选择一个属性:
interface TextButtonProps extends Pick<ButtonProps, "backgroundColor"> {
}
但这不是我在这里会做的,我会简单地通过附加属性扩展ButtonProps接口:
interface TextButtonProps extends ButtonProps {
  textColor?: Colors;
}
然后你的组件将是:
const TextButton: React.FC<TextButtonProps> = ({ children, textColor, ...buttonProps }) => (
  <Button {...buttonProps}>
    <Text color={textColor}>{children}</Text>
  </Button>
);
额外的注释:如果你使用React.FC,你不需要声明children(我强烈建议始终这样做)。请注意,React 18的类型在一定程度上有所改变,因此你可能需要使用React.FC<React.PropsWithChildren<ButtonProps>>。
还有一个:请记住,type T = { ... } & V与interface T extends V { ... }不同(即使在大多数情况下,你可能不关心这种差异)。
英文:
You could keep your types as-is and pass the properties down to Button:
const TextButton: React.FC<TextButtonProps> = ({ textColor, buttonProps }) => (
  <Button {...buttonProps}>
    <Text color={textColor}>...</Text>
  </Text>
);
If you need to use, for example, ButtonProps.backgroundColor inside <TextButton> then you'd need to write something like this <Text foreground={textColor} background={buttonProps?.backgroundColor}>. I, personally, find TextButtonProps.buttonProps a bit confusing: think about the caller:
<TextButton textColor="red" buttonProps={{ backgroundColor: "green" }}>
  Click me
</TextButton>
Truly confusing.
As a note: you can pick a property from a type:
interface TextButtonProps extends Pick<ButtonProps, "backgroundColor"> {
}
But it's NOT what I'd do here, I'd simply extend the ButtonProps interface with the additional properties:
interface TextButtonProps extends ButtonProps {
  textColor?: Colors;
}
Your component will then be:
const TextButton: React.FC<TextButtonProps> = ({ children, textColor, ...buttonProps }) => (
  <Button {...buttonProps}>
    <Text color={textColor}>{children}</Text>
  </Text>
);
<hr>
An extra note: you do not need to declare children if you use React.FC (I strongly suggest to always do it). Note that types for React 18 change a bit so you might need to use React.FC<React.PropsWithChildren<ButtonProps>>.
Another one: remember that type T = { ... } & V is NOT the same as interface T extends V { ... } (even if, in most cases, you probably do not care about the difference).
答案2
得分: 0
你可以使用交集(intersection)来声明具有附加属性的类型:
type TextButtonProps = ButtonProps & {
    textColor?: Colors
}
英文:
If you want to declare a type that has additional properties, you can use an intersection:
type TextButtonProps = ButtonProps & {
    textColor?: Colors
}
答案3
得分: 0
试试这个,不创建额外的类型定义:
TextButtonProps & TextButtonProps[keyof TextButtonProps] 作为类型,它应该包括嵌套的类型。
const TextButton = ({
      backgroundColor,
      textColor = 'black',
      textProps,
      ...buttonProps,
}: TextButtonProps & TextButtonProps[keyof TextButtonProps] ) => {
英文:
Try this without creating an extra type definition:
TextButtonProps & TextButtonProps[keyof TextButtonProps] as type, it should take the nested types too
const TextButton = ({
      backgroundColor,
      textColor = 'black',
      textProps,
      ...buttonProps,
}: TextButtonProps & TextButtonProps[keyof TextButtonProps] ) => {
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论