Skip to content

Commit

Permalink
Fix react-emotion typing (#678)
Browse files Browse the repository at this point in the history
**What**: Fixing react-emotion typing

**Why**: Current typing is redundant.

**How**: Using `create-emotion-styled` typing

**Checklist**:
- [N/A] Documentation
- [x] Tests
- [x] Code complete

I will send a PR for fixing typescript document after this PR is merged.
  • Loading branch information
Ailrun authored and emmatown committed Jun 7, 2018
1 parent e064ff9 commit 185d377
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 128 deletions.
129 changes: 17 additions & 112 deletions packages/react-emotion/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,120 +1,25 @@
// Definitions by: Junyoung Clare Jang <https://github.com/Ailrun>
// TypeScript Version: 2.3
// tslint:disable-next-line:no-implicit-dependencies
import { StatelessComponent, ComponentClass, CSSProperties } from 'react';
import { Interpolation as EmotionInterpolation } from 'emotion';

export * from 'emotion';

export type InterpolationFn<Props = {}> =
(props: Props) =>
| EmotionInterpolation
| InterpolationFn<Props>;

export type InterpolationTypes<Props = {}> =
| InterpolationFn<Props>
| EmotionInterpolation;

export type Interpolation<Props = {}> =
| InterpolationTypes<Props>
| Array<InterpolationTypes<Props>>
| ComponentRef;

export interface Options {
string?: string;
}

type Component<Props> =
| ComponentClass<Props>
| StatelessComponent<Props>;

export type ThemedProps<Props, Theme> = Props & {
theme: Theme,
};

type ElementProps<Tag extends keyof JSX.IntrinsicElements> =
& JSX.IntrinsicElements[Tag]
& { innerRef?: JSX.IntrinsicElements[Tag]['ref'] };

// tslint:disable-next-line:no-empty-interface
interface ComponentRef {}
import {
CreateStyled,
Interpolation,
StyledComponent,
StyledOptions,
Themed,
} from 'create-emotion-styled';

export interface StyledComponent<Props, Theme, IntrinsicProps>
extends
ComponentRef,
ComponentClass<Props & IntrinsicProps>,
StatelessComponent<Props & IntrinsicProps> {
withComponent<Tag extends keyof JSX.IntrinsicElements>(tag: Tag):
StyledComponent<Props, Theme, ElementProps<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: Array<Interpolation<ThemedProps<Props & IntrinsicProps, Theme>>>
): StyledComponent<Props, Theme, IntrinsicProps>;

// overload for object as styles
(
...styles: Array<
| ObjectStyleAttributes
| ((props: ThemedProps<Props & IntrinsicProps, Theme>) => ObjectStyleAttributes)
>
): StyledComponent<Props, Theme, IntrinsicProps>;
}
export * from 'emotion';

// 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: Array<Interpolation<ThemedProps<Props & JSX.IntrinsicElements[Tag], Theme>>>
): StyledComponent<Props, Theme, ElementProps<Tag>>
export type ThemedReactEmotionInterface<Theme extends object> = CreateStyled<Theme>;

// overload for object as styles
<Props = {}>(
...styles: Array<
| ObjectStyleAttributes
| ((props: ThemedProps<Props & JSX.IntrinsicElements[Tag], Theme>) => ObjectStyleAttributes)
>
): StyledComponent<Props, Theme, ElementProps<Tag>>
};
export {
CreateStyled,
Interpolation,
StyledComponent,
StyledOptions,
Themed,
};

export interface ThemedReactEmotionInterface<Theme> extends ShorthandsFactories<Theme> {
// overload for dom tag
<Props, Tag extends keyof JSX.IntrinsicElements>(
tag: Tag,
options?: Options,
// tslint:disable-next-line:no-unnecessary-generics
): CreateStyled<Props, Theme, ElementProps<Tag>>;

// overload for component
<Props, CustomProps>(
component: Component<Props>,
options?: Options,
// tslint:disable-next-line:no-unnecessary-generics
): CreateStyled<Props & CustomProps, Theme, {}>;
}

export interface ThemedReactEmotionModule<Theme> {
default: ThemedReactEmotionInterface<Theme>;
}

declare const styled: ThemedReactEmotionInterface<any>;
declare const styled: CreateStyled;
export default styled;
18 changes: 9 additions & 9 deletions packages/react-emotion/types/tests.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// tslint:disable-next-line:no-implicit-dependencies
import React from 'react';
import styled, { flush, ThemedReactEmotionInterface } from '../';
import styled, { flush, CreateStyled } from '../';

let Component;
let mount;
Expand Down Expand Up @@ -42,7 +42,7 @@ Component = styled.div<CustomProps>(
);
mount = <Component lookColor="red" />;

Component = styled<CustomProps, 'div'>('div')(
Component = styled('div')<CustomProps>(
{ color: 'blue' },
props => ({
background: props.lookColor,
Expand All @@ -51,8 +51,8 @@ Component = styled<CustomProps, 'div'>('div')(
mount = <Component lookColor="red" />;

const anotherColor = 'blue';
Component = styled<CustomProps, 'div'>('div')`
background: ${props => props.lookColor};
Component = styled('div')`
background: ${(props: CustomProps) => props.lookColor};
color: ${anotherColor};
`;
mount = <Component lookColor="red" />;
Expand All @@ -77,21 +77,21 @@ mount = <Component foo="bar" />;
Component = styled(SFCComponent)`color: red`;
mount = <Component foo="bar" />;

Component = styled(MyClassC) ``;
Component = styled(MyClassC)``;
mount = <Component customProp="abc" />;

// do not infer SFCComponentProps with pass CustomProps, need to pass both
Component = styled<SFCComponentProps, CustomProps2>(SFCComponent)({
Component = styled(SFCComponent)<CustomProps2>({
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<SFCComponentProps, CustomProps2>(SFCComponent)`
Component = styled(SFCComponent)`
color: red;
background: ${props => props.customProp};
background: ${(props: CustomProps2) => props.customProp};
`;
mount = <Component customProp="red" foo="bar" />;

Expand All @@ -106,7 +106,7 @@ interface Theme {
};
}

const _styled = styled as ThemedReactEmotionInterface<Theme>;
const _styled = styled as CreateStyled<Theme>;

Component = _styled.div`
color: ${props => props.theme.color.primary}
Expand Down
22 changes: 15 additions & 7 deletions packages/react-emotion/types/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"declaration": true,
"strict": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"baseUrl": "../",
"forceConsistentCasingInFileNames": true,
"jsx": "react",
"lib": ["es6"],
"lib": [
"es6",
"dom"
],
"module": "commonjs",
"noEmit": true,
"noImplicitAny": true,
"noImplicitThis": true,
"strict": true,
"strictNullChecks": true,
"strictFunctionTypes": true
"strictFunctionTypes": true,
"target": "es5",
"typeRoots": [
"../"
],
"types": []
},
"include": [
"./*.ts",
Expand Down

0 comments on commit 185d377

Please sign in to comment.