or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-styling.mdcss-utilities.mdindex.mdreact-native.mdserver-side-rendering.mdtest-utilities.mdtheming.mdtypescript-integration.md
tile.json

typescript-integration.mddocs/

TypeScript Integration

styled-components provides comprehensive TypeScript support with advanced type definitions, generic type preservation, theme typing, and full IntelliSense support for enhanced developer experience.

Basic TypeScript Usage

Typed Styled Components

// Basic component with props
interface ButtonProps {
  primary?: boolean;
  size?: 'small' | 'medium' | 'large';
  disabled?: boolean;
}

const Button = styled.button<ButtonProps>`
  background-color: ${props => props.primary ? '#007bff' : '#6c757d'};
  padding: ${props => {
    switch (props.size) {
      case 'small': return '4px 8px';
      case 'large': return '12px 24px';
      default: return '8px 16px';
    }
  }};
  opacity: ${props => props.disabled ? 0.6 : 1};
  cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
`;

// Usage with full type safety
<Button primary size="large" disabled={false}>Click me</Button>

Component Type Inference

// styled-components automatically infers component props
const Input = styled.input<{ hasError?: boolean }>`
  border-color: ${props => props.hasError ? 'red' : '#ccc'};
`;

// TypeScript knows all input props are available
<Input
  type="email"           // ✅ Valid input prop
  placeholder="Email"    // ✅ Valid input prop
  hasError={true}        // ✅ Custom prop
  onChange={handleChange} // ✅ Valid input prop
  customProp="invalid"   // ❌ TypeScript error
/>

Theme Typing

Declaring Theme Interface

// styled-components.d.ts
import 'styled-components';

interface AppTheme {
  colors: {
    primary: string;
    secondary: string;
    background: string;
    text: string;
    error: string;
    success: string;
    warning: string;
    info: string;
  };
  fonts: {
    main: string;
    mono: string;
    sizes: {
      small: string;
      medium: string;
      large: string;
    };
  };
  spacing: {
    xs: string;
    sm: string;
    md: string;
    lg: string;
    xl: string;
  };
  breakpoints: {
    mobile: string;
    tablet: string;
    desktop: string;
  };
  zIndex: {
    dropdown: number;
    modal: number;
    tooltip: number;
    overlay: number;
  };
  shadows: {
    light: string;
    medium: string;
    heavy: string;
  };
}

declare module 'styled-components' {
  export interface DefaultTheme extends AppTheme {}
}

Typed Theme Usage

// All theme properties are now type-safe
const ThemedButton = styled.button`
  background-color: ${props => props.theme.colors.primary}; // ✅ Type-safe
  font-family: ${props => props.theme.fonts.main}; // ✅ Type-safe
  padding: ${props => props.theme.spacing.md}; // ✅ Type-safe
  box-shadow: ${props => props.theme.shadows.medium}; // ✅ Type-safe
  z-index: ${props => props.theme.zIndex.dropdown}; // ✅ Type-safe
  
  // TypeScript will catch typos
  color: ${props => props.theme.colors.primaryColor}; // ❌ TypeScript error
`;

// Theme utilities with proper typing
function getColor(theme: DefaultTheme, color: keyof AppTheme['colors']): string {
  return theme.colors[color];
}

function getSpacing(theme: DefaultTheme, size: keyof AppTheme['spacing']): string {
  return theme.spacing[size];
}

Advanced Type Patterns

Generic Styled Components

// Generic component that preserves type information
interface ListProps<T> {
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
  keyExtractor: (item: T) => string;
}

const StyledList = styled.div`
  display: flex;
  flex-direction: column;
  gap: 8px;
`;

const ListItem = styled.div`
  padding: 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
`;

function TypedList<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <StyledList>
      {items.map((item, index) => (
        <ListItem key={keyExtractor(item)}>
          {renderItem(item, index)}
        </ListItem>
      ))}
    </StyledList>
  );
}

// Usage with full type safety
interface User {
  id: string;
  name: string;
  email: string;
}

const users: User[] = [
  { id: '1', name: 'Alice', email: 'alice@example.com' },
  { id: '2', name: 'Bob', email: 'bob@example.com' },
];

<TypedList
  items={users}                           // T is inferred as User
  keyExtractor={user => user.id}          // user is typed as User
  renderItem={user => <span>{user.name}</span>} // user is typed as User
/>

Conditional Props with TypeScript

// Discriminated union types for conditional props
interface BaseButtonProps {
  children: React.ReactNode;
  disabled?: boolean;
}

interface LinkButtonProps extends BaseButtonProps {
  variant: 'link';
  href: string;
  target?: '_blank' | '_self';
}

interface RegularButtonProps extends BaseButtonProps {
  variant: 'primary' | 'secondary';
  onClick: () => void;
  type?: 'button' | 'submit' | 'reset';
}

type ButtonProps = LinkButtonProps | RegularButtonProps;

const Button = styled.button<ButtonProps>`
  padding: 12px 24px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  
  background-color: ${props => {
    if (props.variant === 'link') return 'transparent';
    if (props.variant === 'primary') return '#007bff';
    return '#6c757d';
  }};
  
  color: ${props => props.variant === 'link' ? '#007bff' : 'white'};
  text-decoration: ${props => props.variant === 'link' ? 'underline' : 'none'};
`;

// Usage with proper type checking
<Button variant="primary" onClick={() => {}}>Primary Button</Button>
<Button variant="link" href="/about" target="_blank">Link Button</Button>
<Button variant="secondary" href="/invalid">Invalid - TypeScript error</Button>

Polymorphic Components

// Polymorphic component with proper typing
interface PolymorphicProps<T extends React.ElementType> {
  as?: T;
  children: React.ReactNode;
}

type PolymorphicComponentProps<T extends React.ElementType, Props = {}> = 
  PolymorphicProps<T> & 
  Props & 
  Omit<React.ComponentPropsWithoutRef<T>, keyof (PolymorphicProps<T> & Props)>;

interface BoxOwnProps {
  variant?: 'default' | 'elevated' | 'outlined';
  padding?: keyof AppTheme['spacing'];
}

const StyledBox = styled.div<BoxOwnProps>`
  padding: ${props => props.theme.spacing[props.padding || 'md']};
  border-radius: 8px;
  
  ${props => {
    switch (props.variant) {
      case 'elevated':
        return `
          background: white;
          box-shadow: ${props.theme.shadows.medium};
        `;
      case 'outlined':
        return `
          background: transparent;
          border: 1px solid ${props.theme.colors.border};
        `;
      default:
        return `
          background: ${props.theme.colors.background};
        `;
    }
  }}
`;

function Box<T extends React.ElementType = 'div'>({
  as,
  children,
  ...props
}: PolymorphicComponentProps<T, BoxOwnProps>) {
  const Component = as || 'div';
  return <StyledBox as={Component} {...props}>{children}</StyledBox>;
}

// Usage with full type safety for different elements
<Box>Default div box</Box>
<Box as="section" variant="elevated">Section box</Box>
<Box as="a" href="/link" variant="outlined">Link box</Box>
<Box as="button" onClick={() => {}} disabled>Button box</Box>

Utility Types

Custom Interpolation Types

// Custom interpolation function with proper typing
type ThemeFunction<Props = {}> = (props: Props & { theme: DefaultTheme }) => string | number;

interface StyledInterpolation<Props = {}> {
  (props: Props & { theme: DefaultTheme }): string | number | false | undefined | null;
}

// Helper for creating typed interpolations
function createThemeFunction<Props = {}>(
  fn: (theme: DefaultTheme, props: Props) => string | number
): ThemeFunction<Props> {
  return (allProps) => {
    const { theme, ...props } = allProps;
    return fn(theme, props as Props);
  };
}

// Usage
interface ColorProps {
  variant: 'primary' | 'secondary' | 'danger';
}

const getButtonColor = createThemeFunction<ColorProps>((theme, props) => {
  switch (props.variant) {
    case 'primary': return theme.colors.primary;
    case 'secondary': return theme.colors.secondary;
    case 'danger': return theme.colors.error;
    default: return theme.colors.primary;
  }
});

const TypedButton = styled.button<ColorProps>`
  background-color: ${getButtonColor};
  color: white;
  padding: ${props => props.theme.spacing.md};
`;

Props Filtering Types

// Type-safe prop filtering
interface FilteredProps {
  variant: 'primary' | 'secondary';
  size: 'small' | 'large';
  customProp: string;
}

const shouldForwardProp = <T extends keyof FilteredProps>(
  prop: string,
  defaultValidatorFn: (prop: string) => boolean
): prop is T => {
  const customProps: (keyof FilteredProps)[] = ['variant', 'size', 'customProp'];
  return !customProps.includes(prop as keyof FilteredProps) && defaultValidatorFn(prop);
};

const FilteredButton = styled.button.withConfig({
  shouldForwardProp,
})<FilteredProps>`
  background-color: ${props => props.variant === 'primary' ? '#007bff' : '#6c757d'};
  padding: ${props => props.size === 'small' ? '4px 8px' : '12px 24px'};
`;

Component Interface Types

Styled Component Type Definitions

// Interface for styled component instances
interface IStyledComponent<Target, Props = {}> extends React.ComponentType<Props> {
  styledComponentId: string;
  withComponent<NewTarget extends React.ElementType>(
    tag: NewTarget
  ): IStyledComponent<NewTarget, Props>;
  attrs<AttrsProps extends object>(
    attrs: Attrs<AttrsProps & Props>
  ): IStyledComponent<Target, Props & AttrsProps>;
}

// Type for style functions
type StyleFunction<Props extends object> = (
  executionContext: ExecutionContext & Props
) => Interpolation<Props>;

// Interpolation union type
type Interpolation<Props extends object> =
  | string
  | number
  | false
  | undefined
  | null
  | StyleFunction<Props>
  | StyledObject<Props>
  | TemplateStringsArray
  | Keyframes
  | RuleSet<Props>
  | Interpolation<Props>[];

// CSS object type
interface StyledObject<Props extends object> {
  [key: string]:
    | string
    | number
    | StyleFunction<Props>
    | StyledObject<Props>
    | undefined;
}

Component Factory Types

// Factory function types
interface StyledComponentFactory<Target, Props = {}> {
  (
    strings: TemplateStringsArray,
    ...interpolations: Interpolation<Props>[]
  ): IStyledComponent<Target, Props>;
  
  attrs<AttrsProps extends object>(
    attrs: Attrs<AttrsProps & Props>
  ): StyledComponentFactory<Target, Props & AttrsProps>;
  
  withConfig(
    config: StyledOptions<'web', Props>
  ): StyledComponentFactory<Target, Props>;
}

// Helper type for creating component factories
type CreateStyledComponent<Target> = <Props = {}>(
  target: Target
) => StyledComponentFactory<Target, Props>;

Development Tools Integration

React DevTools Integration

// Enable better debugging with displayName
const DebugButton = styled.button.withConfig({
  displayName: 'DebugButton'
})<{ variant: string }>`
  background: ${props => props.variant === 'primary' ? 'blue' : 'gray'};
`;

// Custom hook for styled component debugging
function useStyledDebug<T extends IStyledComponent<any, any>>(
  Component: T,
  displayName: string
) {
  React.useEffect(() => {
    if (process.env.NODE_ENV === 'development') {
      (Component as any).displayName = displayName;
    }
  }, [Component, displayName]);
  
  return Component;
}

Type-Safe CSS Properties

// CSS property validation
interface CSSProperties {
  display?: 'block' | 'inline' | 'flex' | 'grid' | 'none';
  flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse';
  justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around';
  alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline';
  position?: 'static' | 'relative' | 'absolute' | 'fixed' | 'sticky';
  // ... other CSS properties
}

// Type-safe CSS object creation
function createCSSObject<T extends CSSProperties>(styles: T): T {
  return styles;
}

const flexStyles = createCSSObject({
  display: 'flex',
  flexDirection: 'column',
  justifyContent: 'center',
  alignItems: 'center',
  // invalidProperty: 'value' // ❌ TypeScript error
});

const FlexContainer = styled.div`
  ${flexStyles}
`;

Advanced Type Utilities

Extract Props from Styled Components

// Extract props type from styled component
type ExtractProps<T> = T extends IStyledComponent<any, infer P> ? P : never;

const ExampleButton = styled.button<{ variant: string; size: number }>`
  background: blue;
`;

type ButtonProps = ExtractProps<typeof ExampleButton>;
// ButtonProps = { variant: string; size: number }

// Use extracted props in other components
function ButtonWrapper(props: ButtonProps) {
  return <ExampleButton {...props} />;
}

Conditional Styling Utilities

// Type-safe conditional styling helper
function conditionalStyle<Props>(
  condition: (props: Props) => boolean,
  styles: string | ((props: Props) => string)
) {
  return (props: Props) => {
    if (condition(props)) {
      return typeof styles === 'function' ? styles(props) : styles;
    }
    return '';
  };
}

interface ConditionalProps {
  isActive: boolean;
  variant: 'primary' | 'secondary';
}

const ConditionalButton = styled.button<ConditionalProps>`
  padding: 12px 24px;
  border: none;
  
  ${conditionalStyle<ConditionalProps>(
    props => props.isActive,
    'background-color: #28a745; color: white;'
  )}
  
  ${conditionalStyle<ConditionalProps>(
    props => props.variant === 'primary',
    props => `background-color: ${props.theme.colors.primary};`
  )}
`;

Additional Utility Types

Core Utility Types

// Fast omit utility for better performance
type FastOmit<T extends object, U extends string | number | symbol> = {
  [K in keyof T as K extends U ? never : K]: T[K];
};

// Runtime environment type
type Runtime = 'web' | 'native';

// Data attributes helper type
type DataAttributes = { [key: `data-${string}`]: any };

// Polymorphic component interfaces
interface PolymorphicComponent<T extends React.ElementType> {
  <C extends React.ElementType = T>(
    props: PolymorphicComponentProps<C, {}>
  ): React.ReactElement | null;
}

type PolymorphicComponentProps<
  T extends React.ElementType,
  Props = {}
> = Props & 
  Omit<React.ComponentPropsWithoutRef<T>, keyof Props> & {
    as?: T;
  };