【问题标题】:How to add properties to styled component with Typescript?如何使用 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 来解决它,但我不确定这是不是最好的方法。

有没有更好的解决方法?

【问题讨论】:

    标签: reactjs typescript styled-components


    【解决方案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>
      
      

      【讨论】:

        猜你喜欢
        • 2021-07-02
        • 2017-09-01
        • 1970-01-01
        • 2020-09-20
        • 2020-05-02
        • 1970-01-01
        • 2022-12-23
        • 1970-01-01
        • 2017-11-11
        相关资源
        最近更新 更多