带有样式组件的 TypeScript

Posted

技术标签:

【中文标题】带有样式组件的 TypeScript【英文标题】:TypeScript with styled-components 【发布时间】:2019-08-20 02:43:04 【问题描述】:

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 用作开发人员可以提供的一种道具规范,而不必声明由styled-components 等库注入的classNametheme 之类的道具。

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

【问题讨论】:

【参考方案1】:
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 docs:定义组件时,您需要在 Props 界面中将 className 标记为可选

【讨论】:

嗯...声明className 感觉既多余又具有误导性,不是吗?这告诉组件的潜在用户他们可能会设置一个className 属性,在这种情况下组件不会按预期工作,对吧? 我会说这是一个有效的反对意见,但它仍然是官方的做法 这很烦人。我认为在增加样式时应该增加界面。 谢谢您-这个答案很完整,对我帮助很大。干得好!【参考方案2】:

这是一个使用 Styled Components v5 和 React Native 的综合方法,它也适用于普通的 React。如果您没有使用主题,可以跳到底部的StyledProps 部分。

定义您的主题类型。

// MyTheme.ts

export type MyTheme = 
  colors: 
    primary: string;
    background: string;
  ;
;

在您的主题上使用类型。

// themes.ts

export const LightTheme: MyTheme = 
  colors: 
    primary: 'white',
    background: 'white',
  ,
;

export const DarkTheme: MyTheme = 
  colors: 
    primary: 'grey',
    background: 'black',
  ,
;

使用declaration merging 将 MyTheme 类型“合并”到 Styled Components 默认主题中。

// 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 属性输入正确。 组件本身呢?

将您的特定组件道具包装在 StyledProps 类型中。

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 类型或特定组件类型时,它应该可以正常工作。 ?

【讨论】:

它实际上帮助了我很多!谢谢 用户注意:将文件命名为styled-components.d.ts 会导致它完全覆盖@types 定义。相反,您应该将其命名为 styled.d.ts 或将声明与其他全局类型放在 index.d.ts【参考方案3】:

这里发生了很多事情。

props.theme 不会正确输入,除非您在这样的地方增加了 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.

希望这会有所帮助。

编辑:在打字稿中,proptypes 也是无用的。

【讨论】:

以上是关于带有样式组件的 TypeScript的主要内容,如果未能解决你的问题,请参考以下文章

无法使用带有样式组件的关键帧设置嵌套动画的样式

使用带有样式组件的 Javascript 变量

带有样式组件的 TypeScript

如何使用带有样式组件的逻辑类

使用带有样式组件的 ReactCSSTransitionGroup

带有主题的酶渲染样式组件