如何使用 Typescript 向样式化组件添加属性?

Posted

技术标签:

【中文标题】如何使用 Typescript 向样式化组件添加属性?【英文标题】:How to add properties to styled component with Typescript? 【发布时间】:2019-07-15 17:05:51 【问题描述】:

我有这样的组件

import React from 'react';

import styled from '../../styled-components';

const StyledInput = styled.input`
    display: block;
    padding: 5px 10px;
    width: 50%;
    border: none;
    border-radius: 10px;
    outline: none;
    background: $(props) => props.theme.color.background;
    font-family: 'Roboto', sans-serif;
`
;

export const Input = () => 
    return <StyledInput placeholder="All notes"></StyledInput>

我想像这样将它们作为“索引”组件的属性放置

import styled from 'styled-components';

import  Input  from './Input';

const NoteTags:any = styled.div`
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 20%;
    height: 5%;
    border-bottom: 2px solid $(props) => props.theme.color.background; `;

NoteTags.Input = Input;

export default NoteTags;

那么我可以像这样使用它们

import React from 'react';

import NoteTags from '../../blocks/note-tags';

const ComponentNoteTags = () => 
    return (
        <React.Fragment>
            <NoteTags.Input></NoteTags.Input>
        </React.Fragment>
    )


export default ComponentNoteTags;

问题在于NoteTags 上不存在属性Input,所以打字稿给我一个错误。我可以通过将类型 any 设置为 NoteTags 来解决它,但我不确定这是不是最好的方法。

有没有更好的解决方法?

【问题讨论】:

【参考方案1】:

更新

我发现使用类型助手将额外的属性添加到组件的变量中会更好:

// type-helper.ts
export function withProperties<A, B>(component: A, properties: B): A & B 
  Object.keys(properties).forEach(key => 
    component[key] = properties[key]
  );
  return component as A & B;

注意:@Allan 在评论中指出,类型断言可能是必要的,如下所示:

(component as any)[key] = (properties as any)[key]

避免打字稿抱怨Element implicitly has an 'any' type because type '' has no index signature

我们只是将properties 中的每个属性传递给component,并为其赋予两者的连接类型。然后我们可以将子组件“粘合”到主组件中,如下所示:

// component/index.ts
import MainComponent from './MainComponent';
import SubComponent1 from './SubComponent1';
import SubComponent2 from './SubComponent2';
import  withProperties  from '../type-helper.ts';

export withProperties(MainComponent,  SubComponent1, SubComponent2 )

照常使用

import MainComponent from './Component';

export default () => (
  <MainComponent>
    <SubComponent1 />
    <SubComponent2 />
  </MainComponent>
)

这种方法也适用于普通的 React 组件,而不仅仅是样式化的组件,所以我认为它更好。

这是一个codesandbox demo with both approaches I've shared,每个人都使用你的代码。


上一个答案

假设您使用的是最新的软件包:

"styled-components": "^4.1.3",
"@types/styled-components": "^4.1.10",

您可以创建一个interection type,其中包含父组件和子组件的类型:

import styled,  StyledComponent  from 'styled-components'
import ChildComponent from '...'

type Component = StyledComponent<'div', any> & 
    ChildComponent: /* your component type */


interface IProps 
  ...


// create styled component per usual
const MyComponent = styled.div<IProps>`
  ...
` as Component

MyComponent.ChildComponent = ChildComponent;

这是StyledComponent definition:

export type StyledComponent<
    C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
    T extends object,       // theme interface
    O extends object = ,  // props interface
    A extends keyof any = never
> = string & StyledComponentBase<C, T, O, A>;

您可以将其用作StyledComponent&lt;C, T, O, A&gt;StyledComponent&lt;C, T&gt;

【讨论】:

【参考方案2】:

我已经设法通过定义样式化组件的调用签名和您要添加的其他属性来使其工作。 Typescript 会抱怨List 上不存在Item,所以我不得不使用 Object Assign 来创建样式化组件并同时分配属性。

import styled from 'styled-components'
import  PropsWithChildren, ReactElement  from 'react'

interface ListOverload 
    ( children : PropsWithChildren<>): ReactElement
    Item( children : PropsWithChildren<>): ReactElement


export const Item = styled.li(
    ( theme ) => css`
        padding: $theme.space.md;
        border-bottom: 1px solid $theme.colors.greyLight;
    `
)

export const List: ListOverload = Object.assign(
    styled.ul`
        list-style: none;
        padding: 0;
        margin: 0;
    `,
     Item 
)

然后你可以在你的 JSX 中这样使用:

<List>
  <List.Item>Todo 1</List.Item>
  <List.Item>Todo 2</List.Item>
  <List.Item>Todo 3</List.Item>
</List>

【讨论】:

以上是关于如何使用 Typescript 向样式化组件添加属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Material UI 和 TypeScript 将自定义道具传递给样式化组件?

如何在使用 TypeScript 的 create-react-app 中使用带有 babel 插件的样式化组件?

样式化组件 + Typescript:如何键入 css 辅助函数?

无法更改样式化组件主题提供程序

带有 TypeScript 的样式化组件 .attrs

如何使用 Typescript 在不丢失任何道具的情况下键入样式组件?