14

TypeScript newbie here. I have a below component using styled-components that I would like to convert to TypeScript.

import React from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' const propTypes = { name: PropTypes.string.isRequired // https://material.io/tools/icons/?style=baseline } const Icon = styled(({name, className, ...props}) => <i className={`material-icons ${className}`} {...props}>{name}</i>)` font-size: ${props => props.theme.sizeLarger}; ` Icon.propTypes = propTypes export default Icon 

I know I can replace my propTypes with an interface

interface Props { name: string } 

However, TypeScript complains that I leave className undeclared. The thing is, I would ideally like to use the interface as a sort of spec for props that a developer can provide, without having to declare props like className or theme which are injected by libraries like styled-components.

How do I properly convert this component to TypeScript?

3 Answers 3

12
import React from "react"; import styled from "styled-components"; interface Props { name: string; className?: string; } const NonStyledIcon: React.SFC<Props> = ({ name, className, ...props }) => ( <i className={`material-icons ${className}`} {...props}> {name} </i> ); const Icon = styled(NonStyledIcon)` font-size: ${props => props.theme.sizeLarger}; `; export default Icon; 

As per the styled-components TypeScript docs: when defining a component you will need to mark className as optional in your Props interface

Sign up to request clarification or add additional context in comments.

4 Comments

Hm... declaring className feels both redundant and misleading though, doesn't it? This tells a potential user of the component that they may set a className prop, in which case the component wouldn't work as expected, right?
I'd say that's a valid objection, but it's still the official way of doing it
That's annoying. I think the interface should be augmented when you are augmenting the style.
Thank you - this answer is complete and helped me pretty much. Well done!
6

Here's a comprehensive approach using Styled Components v5 with React Native that will work with plain React as well. If you're not using a theme you can skip to the StyledProps section at the bottom.

  • Define your theme type.

    // MyTheme.ts export type MyTheme = { colors: { primary: string; background: string; }; }; 
  • Use the type on your themes.

    // themes.ts export const LightTheme: MyTheme = { colors: { primary: 'white', background: 'white', }, }; export const DarkTheme: MyTheme = { colors: { primary: 'grey', background: 'black', }, }; 
  • Use declaration merging to "merge" the MyTheme type into Styled Components default theme.

    // styled.d.ts import 'styled-components'; import { MyTheme } from '../src/themes/MyTheme'; declare module 'styled-components' { // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface DefaultTheme extends MyTheme {} } 

OK, cool. The theme prop is correctly typed.
What about the components themselves?

  • Wrap your specific component props in the StyledProps type.

    import { StyledProps } from 'styled-components'; import styled from 'styled-components/native'; type MyViewProps = StyledProps<{ backgroundColor?: string; isAlert?: boolean; }>; const MyView = styled.View( (props: MyViewProps) => ` background-color: ${props.backgroundColor || props.theme.colors.background}; color: ${props.isAlert ? red : props.theme.colors.primary} `, ); 

In this example both props.backgroundColor and props.theme.colors.background will auto-complete. When you update MyTheme type or the specific component type it should just work. 👍

component auto-complete

them auto-complete

2 Comments

It actually helps me a lot !! Thx
User beware: naming the file styled-components.d.ts causes it to completely override the @types definitions. Instead you should name it styled.d.ts or put the declaration alongside other global types inside index.d.ts
0

There are many things happening here.

props.theme is not going to typed properlly unless you have the internal type for Theme augmented somewhere like this....

declare module "styled-components" { /* tslint:disable */ export interface DefaultTheme extends YourThemeInterfaceType {} } 

This will change the type of props.theme to YourThemeInterfaceType, if you follow the augmentation documentation properlly but if you can't just create a styled-components.d.ts file and this to your tsconfig.

"include": [ "src/**/*" ], 

Next you need the type of the arguments to know "name" is a string, and className is also i'm assuming a optional string to do this just rewrite the above to this.

interface IIconProps { name: string; className?: string; }; const Icon = styled(({name, className, ...props}: IIconProps) => <i className={`material-icons ${className}`} {...props}>{name}</i>)` font-size: ${props => props.theme.sizeLarger}; `; const test = <Icon name={"xyz"} className={"xyz"}/> // passes typecheck. 

Hope this helps.

EDIT: also proptypes are usless in typescript.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.