TypeScript集成 #
一、基础类型 #
1.1 安装类型定义 #
bash
npm install --save-dev @types/styled-components
1.2 基本类型使用 #
tsx
import styled from 'styled-components';
interface ButtonProps {
primary?: boolean;
size?: 'small' | 'medium' | 'large';
}
const Button = styled.button<ButtonProps>`
padding: ${props =>
props.size === 'small' ? '8px 16px' :
props.size === 'large' ? '16px 32px' :
'12px 24px'
};
background: ${props => props.primary ? '#667eea' : '#fff'};
color: ${props => props.primary ? '#fff' : '#667eea'};
border: 2px solid #667eea;
border-radius: 8px;
`;
function App() {
return (
<>
<Button primary>Primary Button</Button>
<Button size="large">Large Button</Button>
</>
);
}
1.3 扩展 HTML 元素属性 #
tsx
import styled from 'styled-components';
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
$borderColor?: string;
$borderRadius?: string;
}
const Input = styled.input<InputProps>`
padding: 12px 16px;
border: 2px solid ${props => props.$borderColor || '#e0e0e0'};
border-radius: ${props => props.$borderRadius || '8px'};
&:focus {
border-color: #667eea;
outline: none;
}
`;
function App() {
return (
<Input
$borderColor="#667eea"
placeholder="Enter text"
type="text"
/>
);
}
二、主题类型 #
2.1 扩展 DefaultTheme #
tsx
import 'styled-components';
import { DefaultTheme } from 'styled-components';
declare module 'styled-components' {
export interface DefaultTheme {
colors: {
primary: string;
secondary: string;
background: string;
text: string;
error: string;
success: string;
};
fonts: {
main: string;
heading: string;
code: string;
};
spacing: {
[key: number]: string;
};
borderRadius: {
sm: string;
md: string;
lg: string;
};
shadows: {
sm: string;
md: string;
lg: string;
};
}
}
2.2 定义主题 #
tsx
import { DefaultTheme } from 'styled-components';
const lightTheme: DefaultTheme = {
colors: {
primary: '#667eea',
secondary: '#764ba2',
background: '#ffffff',
text: '#333333',
error: '#ff4d4f',
success: '#22c55e',
},
fonts: {
main: 'Inter, sans-serif',
heading: 'Poppins, sans-serif',
code: 'Fira Code, monospace',
},
spacing: {
1: '0.25rem',
2: '0.5rem',
4: '1rem',
6: '1.5rem',
8: '2rem',
},
borderRadius: {
sm: '4px',
md: '8px',
lg: '12px',
},
shadows: {
sm: '0 1px 2px rgba(0, 0, 0, 0.05)',
md: '0 4px 6px rgba(0, 0, 0, 0.1)',
lg: '0 10px 15px rgba(0, 0, 0, 0.1)',
},
};
const darkTheme: DefaultTheme = {
colors: {
primary: '#818cf8',
secondary: '#a78bfa',
background: '#111827',
text: '#f9fafb',
error: '#f87171',
success: '#4ade80',
},
fonts: {
main: 'Inter, sans-serif',
heading: 'Poppins, sans-serif',
code: 'Fira Code, monospace',
},
spacing: {
1: '0.25rem',
2: '0.5rem',
4: '1rem',
6: '1.5rem',
8: '2rem',
},
borderRadius: {
sm: '4px',
md: '8px',
lg: '12px',
},
shadows: {
sm: '0 1px 2px rgba(0, 0, 0, 0.2)',
md: '0 4px 6px rgba(0, 0, 0, 0.3)',
lg: '0 10px 15px rgba(0, 0, 0, 0.4)',
},
};
2.3 使用主题 #
tsx
import styled, { ThemeProvider, useTheme } from 'styled-components';
const Button = styled.button`
padding: ${props => props.theme.spacing[2]} ${props => props.theme.spacing[4]};
background: ${props => props.theme.colors.primary};
color: white;
border: none;
border-radius: ${props => props.theme.borderRadius.md};
`;
const Card = styled.div`
padding: ${props => props.theme.spacing[6]};
background: ${props => props.theme.colors.background};
border-radius: ${props => props.theme.borderRadius.lg};
box-shadow: ${props => props.theme.shadows.md};
`;
function App() {
return (
<ThemeProvider theme={lightTheme}>
<Card>
<Button>Themed Button</Button>
</Card>
</ThemeProvider>
);
}
三、泛型组件 #
3.1 泛型样式组件 #
tsx
import styled from 'styled-components';
interface FlexProps<T = {}> {
direction?: 'row' | 'column';
align?: 'flex-start' | 'center' | 'flex-end' | 'stretch';
justify?: 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around';
gap?: string;
}
const Flex = styled.div<FlexProps>`
display: flex;
flex-direction: ${props => props.direction || 'row'};
align-items: ${props => props.align || 'stretch'};
justify-content: ${props => props.justify || 'flex-start'};
gap: ${props => props.gap || '0'};
`;
function App() {
return (
<Flex direction="column" align="center" gap="16px">
<div>Item 1</div>
<div>Item 2</div>
</Flex>
);
}
3.2 泛型变体组件 #
tsx
import styled, { css } from 'styled-components';
type Variant = 'primary' | 'secondary' | 'danger';
type Size = 'small' | 'medium' | 'large';
interface ButtonProps {
$variant?: Variant;
$size?: Size;
$fullWidth?: boolean;
}
const variantStyles: Record<Variant, ReturnType<typeof css>> = {
primary: css`
background: #667eea;
color: white;
border: none;
`,
secondary: css`
background: transparent;
color: #667eea;
border: 2px solid #667eea;
`,
danger: css`
background: #ff4d4f;
color: white;
border: none;
`,
};
const sizeStyles: Record<Size, ReturnType<typeof css>> = {
small: css`
padding: 8px 16px;
font-size: 14px;
`,
medium: css`
padding: 12px 24px;
font-size: 16px;
`,
large: css`
padding: 16px 32px;
font-size: 18px;
`,
};
const Button = styled.button<ButtonProps>`
${props => variantStyles[props.$variant || 'primary']}
${props => sizeStyles[props.$size || 'medium']}
border-radius: 8px;
cursor: pointer;
transition: all 0.2s;
${props => props.$fullWidth && css`
width: 100%;
`}
`;
四、类型工具 #
4.1 Props 类型提取 #
tsx
import styled from 'styled-components';
type StyledProps<T> = T extends styled.ComponentType<infer P> ? P : never;
const Button = styled.button<{ $primary?: boolean }>`
background: ${props => props.$primary ? '#667eea' : '#fff'};
`;
type ButtonProps = StyledProps<typeof Button>;
4.2 主题类型工具 #
tsx
import { DefaultTheme, useTheme } from 'styled-components';
type ThemeColor = keyof DefaultTheme['colors'];
type ThemeSpacing = keyof DefaultTheme['spacing'];
function useThemeColor(color: ThemeColor): string {
const theme = useTheme();
return theme.colors[color];
}
function useThemeSpacing(spacing: ThemeSpacing): string {
const theme = useTheme();
return theme.spacing[spacing];
}
const ColoredBox = styled.div<{ $color: ThemeColor }>`
background: ${props => props.theme.colors[props.$color]};
`;
4.3 条件类型样式 #
tsx
import styled, { css } from 'styled-components';
type ResponsiveValue<T> = {
base: T;
sm?: T;
md?: T;
lg?: T;
xl?: T;
};
interface ResponsiveProps {
$padding?: ResponsiveValue<string>;
$fontSize?: ResponsiveValue<string>;
}
const applyResponsive = <T,>(
property: string,
value: ResponsiveValue<T> | undefined
) => css`
${value && css`
${property}: ${value.base};
${value.sm && css`@media (min-width: 640px) { ${property}: ${value.sm}; }`}
${value.md && css`@media (min-width: 768px) { ${property}: ${value.md}; }`}
${value.lg && css`@media (min-width: 1024px) { ${property}: ${value.lg}; }`}
${value.xl && css`@media (min-width: 1280px) { ${property}: ${value.xl}; }`}
`}
`;
const Box = styled.div<ResponsiveProps>`
${props => applyResponsive('padding', props.$padding)}
${props => applyResponsive('font-size', props.$fontSize)}
`;
五、类型安全模式 #
5.1 类型安全的变体 #
tsx
import styled, { css } from 'styled-components';
interface VariantConfig<T extends string> {
variants: Record<T, ReturnType<typeof css>>;
defaultVariant: T;
}
function createVariantStyles<T extends string>(
config: VariantConfig<T>
): (variant?: T) => ReturnType<typeof css> {
return (variant) => config.variants[variant || config.defaultVariant];
}
const buttonVariants = createVariantStyles({
variants: {
primary: css`background: #667eea; color: white;`,
secondary: css`background: transparent; color: #667eea;`,
danger: css`background: #ff4d4f; color: white;`,
},
defaultVariant: 'primary',
});
const Button = styled.button<{ $variant?: 'primary' | 'secondary' | 'danger' }>`
${props => buttonVariants(props.$variant)}
padding: 12px 24px;
border-radius: 8px;
`;
5.2 类型安全的属性 #
tsx
import styled from 'styled-components';
type CSSPropertyName =
| 'padding'
| 'margin'
| 'fontSize'
| 'fontWeight'
| 'color'
| 'background'
| 'borderRadius';
type StyleProps = Partial<Record<CSSPropertyName, string>>;
const Box = styled.div<StyleProps>`
${props => props.padding && `padding: ${props.padding};`}
${props => props.margin && `margin: ${props.margin};`}
${props => props.fontSize && `font-size: ${props.fontSize};`}
${props => props.fontWeight && `font-weight: ${props.fontWeight};`}
${props => props.color && `color: ${props.color};`}
${props => props.background && `background: ${props.background};`}
${props => props.borderRadius && `border-radius: ${props.borderRadius};`}
`;
六、类型声明文件 #
6.1 全局类型声明 #
tsx
import 'styled-components';
declare module 'styled-components' {
export interface DefaultTheme {
colors: {
primary: string;
secondary: string;
background: string;
text: string;
};
spacing: Record<number, string>;
borderRadius: Record<string, string>;
}
}
declare module 'styled-components' {
export function css(
first: TemplateStringsArray,
...interpolations: SimpleInterpolation[]
): FlattenedInterpolation<object>;
}
6.2 组件类型导出 #
tsx
import styled from 'styled-components';
export interface ButtonProps {
$variant?: 'primary' | 'secondary' | 'danger';
$size?: 'small' | 'medium' | 'large';
$disabled?: boolean;
$loading?: boolean;
}
export const Button = styled.button<ButtonProps>`
padding: 12px 24px;
border-radius: 8px;
cursor: pointer;
${props => props.$disabled && css`
opacity: 0.6;
cursor: not-allowed;
`}
`;
七、最佳实践 #
7.1 使用 transient props #
tsx
import styled from 'styled-components';
interface ButtonProps {
$variant?: 'primary' | 'secondary';
$size?: 'small' | 'medium' | 'large';
}
const Button = styled.button<ButtonProps>`
padding: ${props => props.$size === 'large' ? '16px 32px' : '12px 24px'};
background: ${props => props.$variant === 'primary' ? '#667eea' : '#fff'};
`;
7.2 类型安全的主题访问 #
tsx
import styled, { useTheme } from 'styled-components';
const getColor = (theme: DefaultTheme, color: keyof DefaultTheme['colors']): string => {
return theme.colors[color];
};
const Button = styled.button`
background: ${props => getColor(props.theme, 'primary')};
`;
function Component() {
const theme = useTheme();
const primaryColor = getColor(theme, 'primary');
return <div style={{ color: primaryColor }}>Colored text</div>;
}
八、总结 #
TypeScript 集成要点速查表:
| 要点 | 说明 |
|---|---|
| 安装类型 | @types/styled-components |
| Props 类型 | styled.button<Props> |
| 扩展属性 | extends React.ButtonHTMLAttributes |
| 主题类型 | 扩展 DefaultTheme |
| transient props | $ 前缀避免 DOM 传递 |
| 泛型组件 | <T extends ...> |
下一步:学习 测试策略 掌握组件样式的测试方法。
最后更新:2026-03-28