-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Add types to react-emotion #398
Changes from 5 commits
77f512a
f8841cd
789acc0
71397a1
e43bdd3
b6d3f50
556463e
1d37138
651daf3
c0f6436
ecdcb54
a009aef
ca17714
a48458d
f2e8603
af24683
815dde2
8209aab
f74de2d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "es5", | ||
"module": "es2015", | ||
"strict": true, | ||
"allowSyntheticDefaultImports": true, | ||
"moduleResolution": "node", | ||
"jsx": "react" | ||
}, | ||
"include": [ | ||
"./*.ts", | ||
"./*.tsx" | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import React from 'react'; | ||
import styled, { ThemedReactEmotionInterface } from '../'; | ||
|
||
let Component; | ||
let mount; | ||
|
||
/* | ||
* Inference HTML Tag Props | ||
*/ | ||
Component = styled.div({ color: 'red' }); | ||
mount = <Component onClick={event => event} />; | ||
|
||
Component = styled('div')({ color: 'red' }); | ||
mount = <Component onClick={event => event} />; | ||
|
||
Component = styled.div`color: red;`; | ||
mount = <Component onClick={(e) => e} />; | ||
|
||
Component = styled('div')`color: red;`; | ||
mount = <Component onClick={(e) => e} />; | ||
|
||
Component = styled.a({ color: 'red' }); | ||
mount = <Component href="#" />; | ||
|
||
Component = styled('a')({ color: 'red' }); | ||
mount = <Component href="#" />; | ||
|
||
/* | ||
* Passing custom props | ||
*/ | ||
type CustomProps = { lookColor: string }; | ||
|
||
Component = styled.div<CustomProps>( | ||
{ color: 'blue' }, | ||
props => ({ | ||
background: props.lookColor, | ||
}), | ||
props => ({ | ||
border: `1px solid ${props.lookColor}`, | ||
}), | ||
); | ||
mount = <Component lookColor="red" />; | ||
|
||
Component = styled<CustomProps, 'div'>('div')( | ||
{ color: 'blue' }, | ||
props => ({ | ||
background: props.lookColor, | ||
}), | ||
); | ||
mount = <Component lookColor="red" />; | ||
|
||
const anotherColor = 'blue'; | ||
Component = styled<CustomProps, 'div'>('div')` | ||
background: ${props => props.lookColor}; | ||
color: ${anotherColor}; | ||
` | ||
mount = <Component lookColor="red" />; | ||
|
||
/* | ||
* With other components | ||
*/ | ||
type CustomProps2 = { customProp: string }; | ||
type SFCComponentProps = { className?: string, foo: string }; | ||
|
||
const SFCComponent: React.StatelessComponent<SFCComponentProps> = props => ( | ||
<div className={props.className}>{props.children} {props.foo}</div> | ||
); | ||
|
||
// infer SFCComponentProps | ||
Component = styled(SFCComponent)({ color: 'red' }); | ||
mount = <Component foo="bar" />; | ||
|
||
// infer SFCComponentProps | ||
Component = styled(SFCComponent)`color: red`; | ||
mount = <Component foo="bar" />; | ||
|
||
// do not infer SFCComponentProps with pass CustomProps, need to pass both | ||
Component = styled<CustomProps2 & SFCComponentProps>(SFCComponent)({ | ||
color: 'red', | ||
}, props => ({ | ||
background: props.customProp, | ||
})); | ||
mount = <Component customProp="red" foo="bar" />; | ||
|
||
// do not infer SFCComponentProps with pass CustomProps, need to pass both | ||
Component = styled<CustomProps2 & SFCComponentProps>(SFCComponent)` | ||
color: red; | ||
background: ${props => props.customProp}; | ||
`; | ||
mount = <Component customProp="red" foo="bar" />; | ||
|
||
|
||
/* | ||
* With explicit theme | ||
*/ | ||
|
||
type Theme = { | ||
color: { | ||
primary: string, | ||
secondary: string, | ||
} | ||
}; | ||
|
||
const _styled = styled as ThemedReactEmotionInterface<Theme>; | ||
|
||
Component = _styled.div` | ||
color: ${props => props.theme.color.primary} | ||
`; | ||
mount = <Component onClick={event => event} />; | ||
|
||
/* | ||
* withComponent | ||
*/ | ||
|
||
type CustomProps3 = { | ||
bgColor: string, | ||
}; | ||
|
||
Component = styled.div<CustomProps3>(props => ({ | ||
bgColor: props.bgColor, | ||
})); | ||
|
||
let Link = Component.withComponent('a'); | ||
mount = <Link href="#" bgColor="red" />; | ||
|
||
let Button = Component.withComponent('button'); | ||
mount = <Button type="submit" bgColor="red" />; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { StatelessComponent, Component as ClassComponent, CSSProperties } from 'react' | ||
import { Interpolation as EmotionInterpolation } from 'emotion' | ||
|
||
export type InterpolationFn<Props = {}> = ( | ||
props: Props | ||
) => EmotionInterpolation | ||
|
||
export type Interpolation<Props = {}> = | ||
| InterpolationFn<Props> | ||
| EmotionInterpolation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know much about typescript but I don't think this handles the fact that there can be nested function interpolations and they will receive props. const SomeComponent = styled.div`
display: ${(p) => props => props.display};
color: ${[props => 'hotpink']};
` There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It works on emotion? It doesn't make any sense to me. What's this use case for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know exactly all edge cases of emotion API. I'll try to study it to improve the coverage support. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mitchellhamilton I've added support to these two cases you mentioned. export type InterpolationFn<Props = {}> =
(props: Props) =>
| EmotionInterpolation
| InterpolationFn<Props>
export type InterpolationTypes<Props = {}> =
| InterpolationFn<Props>
| EmotionInterpolation
export type Interpolation<Props = {}> =
| InterpolationTypes<Props>
| InterpolationTypes<Props>[] If you know more cases, just tell me, please. |
||
|
||
export interface Options { | ||
string?: string, | ||
} | ||
|
||
type Component<Props> = | ||
| ClassComponent<Props> | ||
| StatelessComponent<Props> | ||
|
||
export type ThemedProps<Props, Theme> = Props & { | ||
theme: Theme, | ||
} | ||
|
||
export interface StyledComponent<Props, Theme, IntrinsicProps> | ||
extends | ||
ClassComponent<Props & IntrinsicProps>, | ||
StatelessComponent<Props & IntrinsicProps> | ||
{ | ||
withComponent<Tag extends keyof JSX.IntrinsicElements>(tag: Tag): | ||
StyledComponent<Props, Theme, JSX.IntrinsicElements[Tag]> | ||
|
||
withComponent(component: Component<Props>): | ||
StyledComponent<Props, Theme, {}> | ||
|
||
displayName: string | ||
|
||
__emotion_styles: string[] | ||
__emotion_base: string | Component<Props & IntrinsicProps> | ||
__emotion_real: ThemedReactEmotionInterface<Theme> | ||
} | ||
|
||
export type ObjectStyleAttributes = | ||
| CSSProperties | ||
| { [key: string]: ObjectStyleAttributes } | ||
|
||
export interface CreateStyled<Props, Theme, IntrinsicProps> { | ||
// overload for template string as styles | ||
( | ||
strings: TemplateStringsArray, | ||
...vars: Interpolation<ThemedProps<Props & IntrinsicProps, Theme>>[], | ||
): StyledComponent<Props, Theme, IntrinsicProps> | ||
|
||
// overload for object as styles | ||
( | ||
...styles: ( | ||
| ObjectStyleAttributes | ||
| ((props: ThemedProps<Props & IntrinsicProps, Theme>) => ObjectStyleAttributes) | ||
)[] | ||
): StyledComponent<Props, Theme, IntrinsicProps> | ||
} | ||
|
||
// TODO: find a way to reuse CreateStyled here | ||
// for now I needed to repeat all fn types/overloads | ||
type ShorthandsFactories<Theme> = { | ||
[Tag in keyof JSX.IntrinsicElements]: { | ||
// overload for template string as styles | ||
<Props = {}>( | ||
strings: TemplateStringsArray, | ||
...vars: Interpolation<ThemedProps<Props & JSX.IntrinsicElements[Tag], Theme>>[], | ||
): StyledComponent<Props, Theme, JSX.IntrinsicElements[Tag]> | ||
|
||
// overload for object as styles | ||
<Props = {}>( | ||
...styles: ( | ||
| ObjectStyleAttributes | ||
| ((props: ThemedProps<Props & JSX.IntrinsicElements[Tag], Theme>) => ObjectStyleAttributes) | ||
)[] | ||
): StyledComponent<Props, Theme, JSX.IntrinsicElements[Tag]> | ||
}; | ||
}; | ||
|
||
export interface ThemedReactEmotionInterface<Theme> extends ShorthandsFactories<Theme> { | ||
// overload for dom tag | ||
<Props, Tag extends keyof JSX.IntrinsicElements>( | ||
tag: Tag | Component<Props>, | ||
options?: Options, | ||
): CreateStyled<Props, Theme, JSX.IntrinsicElements[Tag]> | ||
|
||
// overload for component | ||
<Props>( | ||
component: Component<Props>, | ||
options?: Options, | ||
): CreateStyled<Props, Theme, {}> | ||
} | ||
|
||
export interface ThemedReactEmotionModule<Theme> { | ||
default: ThemedReactEmotionInterface<Theme> | ||
} | ||
|
||
declare const styled: ThemedReactEmotionInterface<any> | ||
export default styled | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For glamorous we've added declaration: true to help make sure we're exporting everything required for use in libraries microsoft/TypeScript#5938.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! Added.
No warnings
😄