-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How do I provide props to styled elements in Typescript? #630
Comments
@tal Assuming that TypeScript infers the generics correctly — Disclaimer 😉 I haven't used SC with TypeScript yet — you'll just need to type your interpolation argument: const Input = styled.input`
border: ${(p: YourProps) => p.invalid ? 'red' : 'blue'};
` |
Unfortunately it also errors at call site. I need a way to put generics in
I think
…On Thu, Mar 30, 2017 at 7:04 PM Phil Plückthun ***@***.***> wrote:
@tal <https://github.com/tal> Assuming that TypeScript infers the
generics correctly — Disclaimer 😉 I haven't used SC with TypeScript yet
— you'll just need to type your interpolation argument:
const Input = styled.input` border: ${(p: YourProps) => p.invalid ? 'red' : 'blue'};`
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#630 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AABJ2rxp8PI2i2qOGk_Ym6ZHqA4Lz4Xfks5rrDUEgaJpZM4Mu9JF>
.
|
/cc @styled-components/typers any ideas? |
EDIT: Holdup. What I had just happened to fit my usecase. This currently seems to be the best approach, albeit a bit wordy. |
@philpl 's approach worked for me |
I'm using v2 and found this way more convinient for plain containers: function stuc<T>(El: string = 'div') {
type PropType = T & { children?: JSX.Element[], className?: string }
return (props: PropType) => <El className={props.className}>{props.children}</El>
}
const Container = stuc<{customProp?: boolean}>()
const StyledContainer = styled(Container)`
background: ${p => p.customProp ? 'red' : 'blue'};
` |
@beshanoe hopefully we'll have a way to do this in TS (see: microsoft/TypeScript#11947) |
Going to close this as there isn't much we can do right now about this, thanks for discussing! |
This is what I use at the moment: import styled, { StyledFunction } from "styled-components"
interface YourProps {
invalid: boolean
}
const input: StyledFunction<YourProps & React.HTMLProps<HTMLInputElement>> = styled.input
const Input = input`
border: ${p => p.invalid ? 'red' : 'blue'};
` |
@alloy's solution worked great for me I made a function wrapper for it that doesn't really do anything other than let typescript know of the types, but I think it makes the code a little more terse import React from 'react'
import styled, { StyledFunction } from 'styled-components'
function styledComponentWithProps<T, U extends HTMLElement = HTMLElement>(styledFunction: StyledFunction<React.HTMLProps<U>>): StyledFunction<T & React.HTMLProps<U>> {
return styledFunction
}
interface MyProps {
// ...
}
const div = styledComponentWithProps<MyProps, HTMLDivElement>(styled.div) |
Extending @David-Mk's method to get theme types as shown in https://www.styled-components.com/docs/api#typescript // themed-components.ts
import * as styledComponents from 'styled-components';
import { ThemedStyledComponentsModule } from 'styled-components';
import { ITheme } from 'theme'; // custom theme
type StyledFunction<T> = styledComponents.ThemedStyledFunction<T, ITheme>;
function withProps<T, U extends HTMLElement = HTMLElement>(
styledFunction: StyledFunction<React.HTMLProps<U>>,
): StyledFunction<T & React.HTMLProps<U>> {
return styledFunction;
}
const {
default: styled,
css,
injectGlobal,
keyframes,
ThemeProvider,
} = styledComponents as ThemedStyledComponentsModule<ITheme>;
export { css, injectGlobal, keyframes, ThemeProvider, withProps };
export default styled; // component
import styled, { withProps } from 'themed-components';
interface IProps {
active?: boolean;
}
const Container = withProps<IProps, HTMLDivElement>(styled.div)`
height: 100%;
padding-right: 52px;
display: flex;
justify-content: center;
align-items: center;
color: ${props => props.theme.colors.black4};
background-color: ${props => props.active ? props.theme.colors.white : props.theme.colors.grey};
`; |
Another option which allows to bypass explicit element type specification, but looking a bit awkward: import { ThemedStyledFunction } from 'styled-components';
export const withProps = <U>() =>
<P, T, O>(
fn: ThemedStyledFunction<P, T, O>
): ThemedStyledFunction<P & U, T, O & U> => fn; so later: interface ButtonProps {
active: boolean;
}
// / note () here
export const Button = withProps<ButtonProps>()(styled.div)`
color: ${p => p.active ? 'red' : 'black'}
`; |
@Igorbek Have you noticed that vscode's typescript styled plugin seems to not work when using any of these syntaxes? Edit: In the mean time I've aliased import * as themed from 'styled-components';
//...
interface IHeadingProps {
active?: boolean;
}
const styled = {
div: withProps<IHeadingProps>()(themed.div)
};
//...
export const Heading = styled.div`
font-size: 22px;
`; Edit: Opened issue here: microsoft/typescript-styled-plugin#21 |
Also @Igorbek do you have a full example of your Jul 23 post? TS keeps thinking the types are JSX on that one for me and complaining about implicit any. EDIT: Nevermind, I rewrote it this way and it worked function withProps<U>() {
return <P,T,O>(
fn: ThemedStyledFunction<P, T, O>
): ThemedStyledFunction<P & U, T, O & U> => fn
} |
@Igorbek I needed to slightly change your Reason: there's new "Strict function types" behavior in TS 2.6 which forbids returning My solution was to explicitly cast
Other option is to set |
It seems this methods works too. const Component = styled<Props, 'div'>('div')`
color: ${color.primary}
` And this provides better code-highlighting in code editors, babel-plugins display name supports. |
@FourwingsY I think the documentation mentions you'd want to avoid that
|
@seivan I know, but then i need to wait until webstorm plugin and babel plugin OR styled-components itself supports for |
@FourwingsY I wonder if this would work for you interface SectionProps {
// see https://github.com/Microsoft/TypeScript/issues/8588
children?: React.ReactChild;
className?: string;
}
class PlainButton extends React.PureComponent<SectionProps, {}> {
render() {
return (
<button className={this.props.className}>
{this.props.children}
</button>
);
}
}
const Button = styled(PlainButton)`
/* This renders the buttons above... Edit me! */
border: 2px solid red;
background : "red";
color: palevioletred;
` In other words, you could just build a regular component, and run it through |
@seivan Thanks, I've tried that. class ComplexComponent extends Component<Props, State> {
renderSomeComponent() {
return (
<SomeStyledComponent>
{...}
</SomeStyledComponent>
)
}
render() {
<div className={this.props.className}>
{this.renderSomeComponent()}
<OtherStyledComponent />
{...otherFragments}
</div>
}
} then I need to split It drives me to make too much classes. What i want is styling with typed props, I do not want to split a component into several components just for this. |
@FourwingsY I don't think I follow. Why does it matter how many you got, as you would have to define those smaller components anyway? Unless I'm missing something, nothing would change and it sounds like these are just internal tiny components that shouldn't get exported anyway. But I am just getting started, so most likely I'm missing the point completely. |
@seivan Maybe it's my prefer code style... Yes I might re-think about my codes. Thanks anyway. |
@FourwingsY Let me know if you figure it out. I still haven't figured out a nice way to pass props.
|
Anybody else running into some issues with @David-Mk / @atfzl 's solutions above: import * as styledComponents from 'styled-components';
// tslint:disable
import { ThemedStyledComponentsModule } from 'styled-components';
import { ThemeProps } from './theme';
export type StyledFunction<T> = styledComponents.ThemedStyledFunction<T, ThemeProps>;
function componentWithProps<T, U extends HTMLElement = HTMLElement>(
styledFunction: StyledFunction<React.HTMLProps<U>>
): StyledFunction<T & React.HTMLProps<U>> {
return styledFunction;
}
const {
default: styled,
css,
injectGlobal,
keyframes,
ThemeProvider
} = styledComponents as ThemedStyledComponentsModule<ThemeProps>;
export { css, injectGlobal, keyframes, ThemeProvider, componentWithProps };
export default styled; I'm getting this on the
Versions
|
@goldcaddy77 as @Lucasus pointed out, ts 2.6 introduced strict function types. So I did a function form of his
|
I think styled-components have to decide one of this |
I used this, but as you mentioned there is a lint error with styled-components 4. This worked for me for styled-components 4: |
Most of the examples here make me lose the syntax highlighting on Webstorm/Phpstorm, so I've found this fix: interface ImyComp {
active: boolean
small: boolean
}
const MyComp = styled.div`
${(p: ImyComp) => ``}; // HACK HERE!!!
display: ${p => p.active ? 'block' : 'none'};
` |
@microcipcip
This works for all following occurrences of |
@kuuup-at-work It has a big disadvantage if you later decide to remove that property, you have to move that "hack" to some other property. For this reason, @microcipcip's workaround is kinda better. |
@FredyC
|
What would I do when consuming styled-components that are written in JS in my typescript files? The props are not recognized.. |
After spending a few hours trying to figure out why "moduleResolution": "node" I'm not sure why this fixes the issue, but maybe it will save someone else some time. |
I really struggled with this for a while too, but got it working with some changes to the above solutions. My versions: {
"@types/styled-components": "^4.1.12",
"styled-components": "^4.1.3",
"typescript": "^3.3.3333"
} Custom typed helpers: // utils/styled-components.ts
import * as styledComponents from 'styled-components';
import { ThemedStyledFunction } from 'styled-components';
import { Theme } from './theme';
const {
default: styled,
css,
createGlobalStyle,
keyframes,
ThemeProvider,
} = styledComponents as styledComponents.ThemedStyledComponentsModule<Theme>;
function withProps<U>() {
return <
P extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
T extends object,
O extends object = {}
>(
fn: ThemedStyledFunction<P, T, O>,
): ThemedStyledFunction<P & U, T, O & U> =>
(fn as unknown) as ThemedStyledFunction<P & U, T, O & U>;
}
export { css, createGlobalStyle, keyframes, ThemeProvider, withProps };
export default styled; Example implementation: // components/link.tsx
import { Link } from 'react-router-dom';
import styled, { withProps } from '../../lib/styled-components';
interface LinkStyleProps {
inverse?: boolean;
}
export default withProps<LinkStyleProps>()(styled(Link))`
color: ${props =>
props.inverse
? props.theme.colors.primaryContrast
: props.theme.colors.primary} !important;
`; And an example where you don't pass the custom style props down to the underlying component: import { Link } from 'react-router-dom';
import styled, { withProps } from '../../lib/styled-components';
interface LinkStyleProps {
inverse?: boolean;
}
export default withProps<LinkStyleProps>()(
styled(({ inverse, ...props }) => <Link {...props} />),
)`
color: ${props =>
props.inverse
? props.theme.colors.primaryContrast
: props.theme.colors.primary} !important;
`; |
Can do simply: |
The interface SomeInterface {
awesome: boolean
}
const WrappedText = styled<SomeInterface>(Text)`
color: ${({awesome}) => awesome ? "green" : "black"}
` |
@ni3t Is that a recent thing? When I try that with an interface type I get:
|
Can confirm that @ni3t 's recommendation worked just fine for me. |
Thanks @ni3t 🎈 |
Thanks @ni3t. That helped a lot. @CMCDragonkai I think you tried something like To summarize: // Some properties that we use in our styles,
// but are not part of the underlying component's props.
type MyExtraProps = {
anExtraProp: WhateverType
};
// For basic HTML elements:
const MyStyledComponent = styled.button<MyExtraProps>`
... ${props => props.anExtraProp } ...
`;
// For extending components that support the className prop:
const MyStyledComponent = styled<MyExtraProps>(AStyleableComponent)`
... ${props => props.anExtraProp } ...
`; (P.S. The versions in my |
Based on the above comment and my experience using styled-components with React Native, it seems that |
Ah I've never tried it using the built-in Thanks for the feedback! |
I came across this issue today and found something that worked for my use-case. I wanted to:
Here is how I solved it:
Hope this helps people |
So, I had this:
Which was fine, but when I tried to add a prop type:
Then I get a TypeScript error, because it doesn't recognize Is there a workaround here I'm missing? |
@noelrappin Try this:
What's happening is that the |
For those that find this issue via google, here's the answer in the documentation: Example:
|
i was solving button styled component and this one worked for me import styled, { StyledComponent } from "styled-components" const ButtonContainer: StyledComponent<"button", any, {}, never>= styled.button |
I looked at the docs and other than creating a whole empty component purely for providing a definition of props I'm not sure of if it's possible to provide custom props to a styled element other than theme.
Example
For the above example I don't know how to make it not error.
The text was updated successfully, but these errors were encountered: