TypeScript与styled-components

14

我是TypeScript的新手。我有一个使用styled-components的组件,我想将其转换为TypeScript

import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'


const propTypes = {
  name: PropTypes.string.isRequired // https://material.io/tools/icons/?style=baseline
}

const Icon = styled(({name, className, ...props}) => <i className={`material-icons ${className}`} {...props}>{name}</i>)`
  font-size: ${props => props.theme.sizeLarger};
`

Icon.propTypes = propTypes

export default Icon

我知道可以用 interface 取代我的 propTypes

interface Props {
  name: string
}

然而,TypeScript 抱怨我没有声明 `className`。问题是,我理想情况下希望将 `interface` 用作开发人员可以提供的 props 的规范,而无需像 `className` 或 `theme` 这样的 props 声明,这些 props 是由 `styled-components` 等库注入的。

我该如何正确地将此组件转换为 TypeScript?

3个回答

12
import React from "react";
import styled from "styled-components";

interface Props {
  name: string;
  className?: string;
}

const NonStyledIcon: React.SFC<Props> = ({ name, className, ...props }) => (
  <i className={`material-icons ${className}`} {...props}>
    {name}
  </i>
);

const Icon = styled(NonStyledIcon)`
  font-size: ${props => props.theme.sizeLarger};
`;

export default Icon;

根据 styled-components 的 TypeScript 文档:在定义组件时,您需要在 Props 接口中将 className 标记为可选


嗯...声明 className 似乎既多余又具有误导性,不是吗?这会告诉组件的潜在用户他们可以设置一个 className 属性,而在这种情况下,该组件将不能正常工作,对吧? - artooras
1
我认为这是一个合理的反对意见,但这仍然是官方的做法。 - torkel
很烦人。我认为当你增强样式时,界面应该被增强。 - CMCDragonkai
谢谢 - 这个答案很完整,帮了我很多。做得好! - Mieszczańczyk S.

6
这是使用React Native的Styled Components v5的全面方法,也适用于纯React。如果您没有使用主题,则可以跳过底部的StyledProps部分。
  • Define your theme type.

    // MyTheme.ts
    
    export type MyTheme = {
      colors: {
        primary: string;
        background: string;
      };
    };
    
  • Use the type on your themes.

    // themes.ts
    
    export const LightTheme: MyTheme = {
      colors: {
        primary: 'white',
        background: 'white',
      },
    };
    
    export const DarkTheme: MyTheme = {
      colors: {
        primary: 'grey',
        background: 'black',
      },
    };
    
  • Use declaration merging to "merge" the MyTheme type into Styled Components default theme.

    // styled.d.ts
    
    import 'styled-components';
    import { MyTheme } from '../src/themes/MyTheme';
    
    declare module 'styled-components' {
      // eslint-disable-next-line @typescript-eslint/no-empty-interface
      export interface DefaultTheme extends MyTheme {}
    }
    

好的,很棒。 theme 属性已经正确地类型化了。
那么组件本身呢?

  • Wrap your specific component props in the StyledProps type.

    import { StyledProps } from 'styled-components';
    import styled from 'styled-components/native';
    
    type MyViewProps = StyledProps<{
      backgroundColor?: string;
      isAlert?: boolean;
    }>;
    
    const MyView = styled.View(
      (props: MyViewProps) => `
        background-color: ${props.backgroundColor || props.theme.colors.background};
        color: ${props.isAlert ? red : props.theme.colors.primary}
      `,
    );
    
在这个例子中,props.backgroundColorprops.theme.colors.background 都会自动补全。当你更新 MyTheme 类型或特定组件类型时,它应该可以正常工作。 component auto-complete them auto-complete

1
这实际上对我很有帮助!!谢谢 - user7637140
警告:将文件命名为 styled-components.d.ts 会完全覆盖 @types 的定义。相反,您应该将其命名为 styled.d.ts 或将声明与其他全局类型一起放在 index.d.ts 内。 - Brendan McGill

0

这里发生了很多事情。

除非您在某个地方增强了主题的内部类型,否则props.theme将无法正确输入...

declare module "styled-components" {
    /* tslint:disable */
    export interface DefaultTheme extends YourThemeInterfaceType {}
}

如果您按照增强文档正确操作,这将更改props.theme的类型为YourThemeInterfaceType,否则,只需创建一个styled-components.d.ts文件并将其添加到您的tsconfig中即可。

"include": [
    "src/**/*"
],

接下来,您需要知道参数的类型,以了解“name”是一个字符串,而className也是我假设的可选字符串。为此,请将上面的内容重写为以下形式。
interface IIconProps {
    name: string;
    className?: string;
};

const Icon = styled(({name, className, ...props}: IIconProps) => <i className={`material-icons ${className}`} {...props}>{name}</i>)`
    font-size: ${props => props.theme.sizeLarger};
`;


const test = <Icon name={"xyz"} className={"xyz"}/> // passes typecheck.

希望这可以帮助到你。
编辑:在 TypeScript 中,prop-types 是无用的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接