Skip to content

Commit

Permalink
Merge pull request #1 from JakeGinnivan/topics/global-theme-type
Browse files Browse the repository at this point in the history
Quick pass at removing theme generic param
  • Loading branch information
tomsseisums authored Nov 23, 2019
2 parents 82a7d40 + 5c5cd86 commit 16ae3b7
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 157 deletions.
3 changes: 2 additions & 1 deletion packages/core/types-tests-global-theme/tests.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// TODO: Test if Global implements default theme
// TODO: Test if css prop implements default theme
// TODO: Test if ClassNames implements default theme
// TODO: Test if utilities can be re-exported with custom theme and they respect the custom theme (Global -> implements the interface for styles callback; css prop -> implements the interface for callback; ClassNames -> implements the interface for css, theme)
// TODO: Test if utilities can be re-exported with custom theme and they respect the custom theme
// (Global -> implements the interface for styles callback; css prop -> implements the interface for callback; ClassNames -> implements the interface for css, theme)
throw new Error('Tests not yet implemented')
31 changes: 11 additions & 20 deletions packages/core/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ export {
ObjectInterpolation
} from '@emotion/css'

export type AnyIfEmpty<T extends object> = keyof T extends never ? any : T

export { EmotionCache, Interpolation, SerializedStyles, css }

export const ThemeContext: Context<object>
Expand All @@ -36,20 +34,15 @@ export function withEmotionCache<Props, RefType = any>(

export const jsx: typeof createElement

export type InterpolationWithTheme<Theme> =
| Interpolation
| ((theme: Theme) => Interpolation)

export interface GlobalProps<Theme> {
styles: InterpolationWithTheme<Theme>
export interface GlobalProps {
styles: Interpolation<Emotion.Theme>
}

/**
* @desc
* JSX generic are supported only after TS@2.9
*/
export function Global<Theme extends {} = AnyIfEmpty<Emotion.Theme>>(
props: GlobalProps<Theme>
): ReactElement
export function Global(props: GlobalProps): ReactElement

export function keyframes(
template: TemplateStringsArray,
Expand All @@ -66,26 +59,24 @@ export type ClassNamesArg =
| { [className: string]: boolean | null | undefined }
| ArrayClassNamesArg

export interface ClassNamesContent<Theme> {
export interface ClassNamesContent {
css(template: TemplateStringsArray, ...args: Array<Interpolation>): string
css(...args: Array<Interpolation>): string
cx(...args: Array<ClassNamesArg>): string
theme: Theme
theme: Emotion.Theme
}
export interface ClassNamesProps<Theme> {
children(content: ClassNamesContent<Theme>): ReactNode
export interface ClassNamesProps {
children(content: ClassNamesContent): ReactNode
}
/**
* @desc
* JSX generic are supported only after TS@2.9
*/
export function ClassNames<Theme extends {} = AnyIfEmpty<Emotion.Theme>>(
props: ClassNamesProps<Theme>
): ReactElement
export function ClassNames(props: ClassNamesProps): ReactElement

declare module 'react' {
interface DOMAttributes<T> {
css?: InterpolationWithTheme<AnyIfEmpty<Emotion.Theme>>
css?: Interpolation
}
}

Expand All @@ -102,7 +93,7 @@ declare global {
*/

interface IntrinsicAttributes {
css?: InterpolationWithTheme<AnyIfEmpty<Emotion.Theme>>
css?: Interpolation
}
}
}
21 changes: 10 additions & 11 deletions packages/core/types/tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ import {
} from '@emotion/core'
;<Global styles={[]} />

interface TestTheme0 {
resetStyle: any
declare global {
namespace Emotion {
interface Theme {
primaryColor: string
secondaryColor: string
}
}
}

;<Global styles={(theme: TestTheme0) => [theme.resetStyle]} />
;<Global styles={theme => [theme.primaryColor]} />

declare const getRandomColor: () => string

Expand Down Expand Up @@ -92,14 +97,8 @@ const anim1 = keyframes`
}}
world="of-world"
/>

interface TestTheme1 {
primaryColor: string
secondaryColor: string
}

;<ClassNames>
{({ css, cx, theme }: ClassNamesContent<TestTheme1>) => {
{({ css, cx, theme }) => {
return (
<div>
<span className={cx('a', undefined, 'b', null, [['abc']])} />
Expand All @@ -112,7 +111,7 @@ interface TestTheme1 {
</span>
<span
className={css`
color: theme.secondaryColor;
color: ${theme.secondaryColor};
`}
>
Snd Text
Expand Down
26 changes: 7 additions & 19 deletions packages/emotion-theming/types-tests-global-theme/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

import * as React from 'react'

import { AnyIfEmpty } from '@emotion/core'

import {
StyledComponent,
StyledOptions,
Expand All @@ -13,37 +11,27 @@ import {
} from '@emotion/styled'
import { PropsOf, DistributiveOmit } from '@emotion/styled-base'

export interface ThemeProviderProps<Theme> {
theme: Partial<Theme> | ((outerTheme: Theme) => Theme)
export interface ThemeProviderProps {
theme: Partial<Emotion.Theme> | ((outerTheme: Emotion.Theme) => Emotion.Theme)
children?: React.ReactNode
}

export interface ThemeProvider<Theme extends {} = AnyIfEmpty<Emotion.Theme>> {
(props: ThemeProviderProps<Theme>): React.ReactElement
export interface ThemeProvider {
(props: ThemeProviderProps): React.ReactElement
}

export type withTheme<Theme extends {} = AnyIfEmpty<Emotion.Theme>> = <
export type withTheme = <
C extends React.ComponentType<React.ComponentProps<C>>
>(
component: C
) => React.FC<DistributiveOmit<PropsOf<C>, 'theme'> & { theme?: Theme }>
) => React.FC<DistributiveOmit<PropsOf<C>, 'theme'> & { theme?: Emotion.Theme }>

export type useTheme<Theme extends {} = AnyIfEmpty<Emotion.Theme>> = <
T extends Theme = Theme
>() => T
export function useTheme(): Emotion.Theme

export const ThemeProvider: ThemeProvider

export const withTheme: withTheme

export const useTheme: useTheme

export interface EmotionTheming<Theme> {
ThemeProvider: ThemeProvider<Theme>
withTheme: withTheme<Theme>
useTheme: useTheme<Theme>
}

export type WithTheme<P, T> = P extends { theme: infer Theme }
? P & { theme: Exclude<Theme, undefined> }
: P & { theme: T }
3 changes: 2 additions & 1 deletion packages/emotion-theming/types-tests-global-theme/tests.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// TODO: Test if withTheme implements default theme
// TODO: Test if useTheme implements default theme
// TODO: Test if ThemeProvider expects default theme
// TODO: Test if utilities can be re-exported with custom theme and they respect the custom theme (withTheme -> component has theme props; useTheme -> has theme props; ThemeProvider -> provides the theme to children)
// TODO: Test if utilities can be re-exported with custom theme and they respect the custom theme
// (withTheme -> component has theme props; useTheme -> has theme props; ThemeProvider -> provides the theme to children)
throw new Error('Tests not yet implemented')
32 changes: 9 additions & 23 deletions packages/emotion-theming/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,32 @@

import * as React from 'react'

import { AnyIfEmpty } from '@emotion/core'

import {
StyledComponent,
StyledOptions,
CreateStyledComponent,
StyledTags
} from '@emotion/styled'
import { PropsOf, DistributiveOmit } from '@emotion/styled-base'
import '@emotion/core'

export interface ThemeProviderProps<Theme> {
theme: Partial<Theme> | ((outerTheme: Theme) => Theme)
export interface ThemeProviderProps {
theme: Partial<Emotion.Theme> | ((outerTheme: Emotion.Theme) => Emotion.Theme)
children?: React.ReactNode
}

export interface ThemeProvider<Theme extends {} = AnyIfEmpty<Emotion.Theme>> {
(props: ThemeProviderProps<Theme>): React.ReactElement
export interface ThemeProvider {
(props: ThemeProviderProps): React.ReactElement
}
export const ThemeProvider: ThemeProvider

export type withTheme<Theme extends {} = AnyIfEmpty<Emotion.Theme>> = <
export function withTheme<
C extends React.ComponentType<React.ComponentProps<C>>
>(
component: C
) => React.FC<DistributiveOmit<PropsOf<C>, 'theme'> & { theme?: Theme }>

export type useTheme<Theme extends {} = AnyIfEmpty<Emotion.Theme>> = <
T extends Theme = Theme
>() => T

export const ThemeProvider: ThemeProvider

export const withTheme: withTheme
): React.FC<DistributiveOmit<PropsOf<C>, 'theme'> & { theme?: Emotion.Theme }>

export const useTheme: useTheme

export interface EmotionTheming<Theme> {
ThemeProvider: ThemeProvider<Theme>
withTheme: withTheme<Theme>
useTheme: useTheme<Theme>
}
export function useTheme(): Emotion.Theme

export type WithTheme<P, T> = P extends { theme: infer Theme }
? P & { theme: Exclude<Theme, undefined> }
Expand Down
77 changes: 19 additions & 58 deletions packages/emotion-theming/types/tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,29 @@ import { Interpolation, ObjectInterpolation } from '@emotion/styled-base'

const { ThemeProvider, withTheme, useTheme } = emotionTheming

interface Theme {
primary: string
secondary: string
declare global {
namespace Emotion {
interface Theme {
primary: string
secondary: string
}
}
}
declare const theme: Theme

declare const theme: Emotion.Theme

interface Props {
prop: boolean
}
declare const CompFC: React.FC<Props>
declare class CompC extends React.Component<Props & { theme: Theme }> {}
declare class CompC extends React.Component<Props & { theme: Emotion.Theme }> {}

const WrappedCompC = withTheme(CompC)
;<ThemeProvider theme={theme}>{WrappedCompC}</ThemeProvider>
;<ThemeProvider theme={() => theme} />
;<ThemeProvider theme={(outerTheme: Theme) => ({ ...outerTheme, ...theme })} />
;<ThemeProvider
theme={(outerTheme: Emotion.Theme) => ({ ...outerTheme, ...theme })}
/>

const ThemedFC = withTheme(CompFC)
;<ThemedFC prop />
Expand All @@ -40,9 +47,7 @@ class CompCWithDefault extends React.Component<Props> {
}

{
const theme: Theme = useTheme()

const themeFail: Theme = useTheme<number>() // $ExpectError
const theme: Emotion.Theme = useTheme()
}

const ThemedFCWithDefault = withTheme(CompFCWithDefault)
Expand All @@ -53,22 +58,6 @@ const ThemedCompWithDefault = withTheme(CompCWithDefault)
;<ThemedCompWithDefault />
;<ThemedCompWithDefault theme={theme} />

const {
ThemeProvider: TypedThemeProvider,
withTheme: typedWithTheme
} = emotionTheming as emotionTheming.EmotionTheming<Theme>
;<TypedThemeProvider theme={theme} />
// $ExpectError
;<TypedThemeProvider theme={{ primary: 5 }} />

typedWithTheme(CompFC)

/**
* @todo
* Following line should report an error.
*/
typedWithTheme((props: { value: number }) => null)

{
interface Book {
kind: 'book'
Expand All @@ -94,36 +83,11 @@ typedWithTheme((props: { value: number }) => null)
;<Readable kind="book" author="Hejlsberg" />
;<ThemedReadable kind="book" author="Hejlsberg" />
;<Readable kind="magazine" author="Hejlsberg" /> // $ExpectError
;<ThemedReadable kind="magazine" author="Hejlsberg" /> // $ExpectError
}

const themedStyled = styled as CreateStyled<Theme>

const StyledCompC = themedStyled(WrappedCompC)({})
const AdditionallyStyledCompC = themedStyled(StyledCompC)({})
;<StyledCompC prop={true} />
;<AdditionallyStyledCompC prop={true} />

const StyledDiv = themedStyled('div')({})
;<StyledDiv />
// $ExpectError
;<StyledDiv theme={{ primary: 0, secondary: 0 }} />
const AdditionallyStyledDiv = themedStyled(StyledDiv)({})
;<AdditionallyStyledDiv />
// $ExpectError
;<AdditionallyStyledDiv theme={{ primary: 0, secondary: 0 }} />

const StyledDiv2 = themedStyled.div({})
;<StyledDiv2 />
// $ExpectError
;<StyledDiv2 theme={{ primary: 0, secondary: 0 }} />

export type StyleDefinition<T = {}> = Interpolation<
emotionTheming.WithTheme<T, Theme>
>
export type ObjectStyleDefinition<T = {}> = ObjectInterpolation<
emotionTheming.WithTheme<T, Theme>
>
export type StyleDefinition = Interpolation
export type ObjectStyleDefinition = ObjectInterpolation<{
theme: Emotion.Theme
}>

const style: StyleDefinition = ({ theme }) => ({
color: theme.primary
Expand All @@ -133,7 +97,4 @@ const style2: ObjectStyleDefinition = {
}

// Can use ThemeProvider
;<ThemeProvider theme={{ prop: 'val' }} />
;<TypedThemeProvider theme={{ primary: '', secondary: '' }} />
// $ExpectError
;<TypedThemeProvider theme={{ nope: 'string' }} />
;<ThemeProvider theme={{ primary: 'val' }} />
Loading

0 comments on commit 16ae3b7

Please sign in to comment.