【问题标题】:Using styled-components with props and TypeScript使用带有 props 和 TypeScript 的 styled-components
【发布时间】:2018-04-15 02:09:39
【问题描述】:

我正在尝试将 TypeScript 集成到我们的项目中,到目前为止,我偶然发现了 styled-components 库的一个问题。

考虑这个组件

import * as React from "react";
import styled from "styled-components/native";
import { TouchableOpacity } from "react-native";

// -- types ----------------------------------------------------------------- //
export interface Props {
  onPress: any;
  src: any;
  width: string;
  height: string;
}

// -- styling --------------------------------------------------------------- //
const Icon = styled.Image`
  width: ${(p: Props) => p.width};
  height: ${(p: Props) => p.height};
`;

class TouchableIcon extends React.Component<Props> {
  // -- default props ------------------------------------------------------- //
  static defaultProps: Partial<Props> = {
    src: null,
    width: "20px",
    height: "20px"
  };

  // -- render -------------------------------------------------------------- //
  render() {
    const { onPress, src, width, height } = this.props;
    return (
      <TouchableOpacity onPress={onPress}>
        <Icon source={src} width={width} height={height} />
      </TouchableOpacity>
    );
  }
}

export default TouchableIcon;

下面一行抛出 3 个错误,本质上是相同的 &lt;Icon source={src} width={width} height={height} /&gt;

输入{来源:任何;宽度:字符串;高度:字符串;} 不可分配 键入 IntrinsicAttributes ...类型中缺少属性“onPress” {来源:任何;宽度:字符串;高度:字符串;}

不完全确定这是什么以及如何解决它,我是否需要在 Icon 或类似的东西上声明这些?

编辑: 打字稿v2.6.1,样式组件v2.2.3

【问题讨论】:

    标签: javascript reactjs typescript jsx styled-components


    【解决方案1】:

    我自己也在努力解决这个问题,但我认为问题在于您在样式化组件中使用 Props 接口。尝试仅使用图像道具创建另一个界面并在您的样式组件中使用它:

    import * as React from "react";
    import styled from "styled-components/native";
    import { TouchableOpacity } from "react-native";
    
    // -- types ----------------------------------------------------------------- //
    export interface Props {
      onPress: any;
      src: any;
      width: string;
      height: string;
    }
    
    
    export interface ImageProps {
      src: string;
      width: string;
      height: string;
    }
    
    // -- styling --------------------------------------------------------------- //
    const Icon = styled.Image`
      width: ${(p: ImageProps ) => p.width};
      height: ${(p: ImageProps ) => p.height};
    `;
    
    class TouchableIcon extends React.Component<Props> {
      // -- default props ------------------------------------------------------- //
      static defaultProps: Partial<Props> = {
        src: null,
        width: "20px",
        height: "20px"
      };
    
      // -- render -------------------------------------------------------------- //
      render() {
        const { onPress, src, width, height } = this.props;
        return (
          <TouchableOpacity onPress={onPress}>
            <Icon source={src} width={width} height={height} />
          </TouchableOpacity>
        );
      }
    }
    
    export default TouchableIcon;
    

    似乎可以工作,但我讨厌必须复制这些接口。希望其他人可以显示正确的方法,或者将 ImageProps 嵌入到 Props 中?

    【讨论】:

    • 这对我不起作用,但 tmp 解决方法是 const Icon: any = styled.Image 我不知道它为什么起作用,但是
    • 很高兴你让它工作,这对我也有用。我在 react-dom 所以也许那里有一些不同。
    • 这就是我需要的:width: ${(p: ImageProps ) =&gt; p.width};
    【解决方案2】:

    这个答案已经过时了,最新的答案在这里:https://stackoverflow.com/a/52045733/1053772

    据我所知,没有官方方法(还没有?)可以做到这一点,但你可以用一些技巧来解决它。首先,创建一个withProps.ts 文件,内容如下:

    import * as React from 'react'
    import { ThemedStyledFunction } from 'styled-components'
    
    const withProps = <U>() => <P, T, O>(fn: ThemedStyledFunction<P, T, O>) =>
        fn as ThemedStyledFunction<P & U, T, O & U>
    
    export { withProps }
    

    现在,在您的 .tsx 文件中,像这样使用它:

    // ... your other imports
    import { withProps } from './withProps'
    
    export interface IconProps {
      onPress: any;
      src: any;
      width: string;
      height: string;
    }
    
    const Icon = withProps<IconProps>()(styled.Image)`
      width: ${(p: IconProps) => p.width};
      height: ${(p: IconProps) => p.height};
    `;
    

    你应该很高兴。这绝对是理想的,希望很快就会有一种方法可以在 TS 中为模板文字提供泛型,但我想目前这是你最好的选择。

    在到期时给予信用:我从here复制粘贴了这个

    【讨论】:

    • 这是一个很好的解决方案,但@elnygren 下面的评论绝对是当前最优雅的解决方案。
    【解决方案3】:

    最近有一些发展,新版本的 Typescript(例如 3.0.1)和样式组件(例如 3.4.5)不需要单独的帮助程序。您可以直接将道具的接口/类型指定给样式化组件。

    interface Props {
      onPress: any;
      src: any;
      width: string;
      height: string;
    }
    
    const Icon = styled.Image<Props>`
      width: ${p => p.width};
      height: ${p => p.height};
    `;
    

    如果您想更精确并忽略onPress

    const Icon = styled.Image<Pick<Props, 'src' | 'width' | 'height'>>`
      width: ${p => p.width};
      height: ${p => p.height};
    `;
    

    【讨论】:

    • 整洁!也适用于主题。这应该是选定的答案。唯一的问题是它破坏了我的语法高亮...
    • 不应该是styled.image&lt;Props&gt;吗?
    • @ManuRauck 我不同意。输入{来源:任何;宽度:字符串; height: string;} 不可分配给类型 IntrinsicAttributes ... {source: any; 类型中缺少属性“onPress”宽度:字符串; height: string;} onPress 明显缺失,所以 onPress 是 Image 的一个 prop
    • 谁能推荐让 Typescript 接受带参数的关键帧的解决方案?
    • 可以使用 styled.Image>` 短一点
    【解决方案4】:

    你只需要指定一个interface:

    import { createGlobalStyle } from 'styled-components';
    
    interface PropsGlobalStyle {
      dark: boolean
    }
    
    export default createGlobalStyle`
      body {
        box-sizing: border-box;
        margin: 0;
        font-family: Arial, Helvetica, sans-serif;
        color: ${(props:PropsGlobalStyled) => (props.dark ? '#FFF' : '#000')};
    background-color: ${(props:PropsGlobalStyled) => (props.dark ? '#000' : '#FFF')};
      }
    `;
    

    【讨论】:

      【解决方案5】:

      样式化组件

          import styled from 'styled-components';
      
      interface Props {
          height: number;
      }
      
      export const Wrapper = styled.div<Props>`
          padding: 5%;
          height: ${(props) => props.height}%;
      `;
      

      索引

      import React, { FunctionComponent } from 'react';
      import { Wrapper } from './Wrapper';
      
      interface Props {
          className?: string;
          title: string;
          height: number;
      }
      
      export const MainBoardList: FunctionComponent<Props> = ({ className, title, height }) => (
          <Wrapper height={height} className={className}>
              {title}
          </Wrapper>
      );
          
      

      应该工作

      【讨论】:

        【解决方案6】:

        使用带有颜色属性的ColorCard 的示例

        import styled from 'styled-components';
        
        export const ColorCard = styled.div<{ color: string }>`
          background-color: ${({ color }) => color};
        `;
        

        【讨论】:

        • 太棒了,谢谢
        • 这应该被接受的答案
        【解决方案7】:

        @elnygren 的回答对我有用。我只有一个问题。如何为以下代码分配默认值(复制自@elnygren 的答案)。例如如果我不想将任何值传递给“宽度”和“高度”,将使用默认值。

        const Icon = styled.Image<Pick<Props, 'src' | 'width' | 'height'>>`
          width: ${p => p.width};
          height: ${p => p.height};
        `;
        

        【讨论】:

        • 如果您有新问题,请点击 按钮提出问题。如果有助于提供上下文,请包含指向此问题的链接。 - From Review
        猜你喜欢
        • 2020-03-02
        • 2018-04-27
        • 2019-02-18
        • 2021-09-22
        • 2021-09-19
        • 2020-06-07
        • 2020-05-28
        • 2021-10-13
        • 2020-02-16
        相关资源
        最近更新 更多