reactjs - 如何使用 Typescript 為樣式化組件添加屬性?
我有像這樣的組件
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>
}
我想像這樣將它們作為屬性放在「index」組件上
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;
這個問題的關鍵在於屬性 Input 在 NoteTags 上不存在,所以 TypeScript 會給我一個錯誤。我可以透過將 NoteTags 的類型設為 any 來解決,但我不確定這是不是最好的方式。
有沒有更好的方法來修理它?
答案#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]為了避免 TypeScript 抱怨
Element 隱式具有 'any' 類型,因為類型 '{}' 沒有索引簽名。
我們只是將 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 範例,展示了我分享的兩種方法,每種方法各一個,請使用你的程式碼。
先前的回答
假設你正在使用最新的套件:
"styled-components": "^4.1.3",
"@types/styled-components": "^4.1.10",
你可以創建一種類型,包含父組件和子組件的類型:
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 的定義:
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<C, T, O, A> 或 StyledComponent<C, T>
答案#2
我已經設法讓它運行,方法是定義樣式化組件的調用簽名以及你想要添加的額外屬性。Typescript 會抱怨 Item 不存在於 List 上,因此我不得不使用 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>
