Skip to content

Commit

Permalink
Added TS types to @emotion/native (#1634)
Browse files Browse the repository at this point in the history
* adding types for react native

* fixing styled component types

* removing condition type inference for existence of a theme

* Move @emotion/native TS types closer to what we have for @emotion/styled

* some minor adjustments to the types

* updating interpolation types to support inferred style type

* addressing some deficiencies

* Use Theme interface from @emotion/core in @emotion/native

* adding review suggestions

* Add changeset
  • Loading branch information
patsissons authored and Andarist committed Dec 16, 2019
1 parent 2fa7a21 commit 456be9a
Show file tree
Hide file tree
Showing 10 changed files with 532 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changeset/tall-weeks-compete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@emotion/native': minor
---

Added TypeScript type definitions.
5 changes: 4 additions & 1 deletion packages/native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
"main": "dist/native.cjs.js",
"module": "dist/native.esm.js",
"scripts": {
"test:typescript": "exit 0"
"test:typescript": "dtslint types"
},
"files": [
"src",
"dist",
"macro.js"
],
"types": "types/index.d.ts",
"devDependencies": {
"@babel/core": "^7.7.2",
"@types/react-native": "^0.57.0",
"dtslint": "^0.3.0",
"react": "^16.11.0",
"react-native": "^0.57.0"
},
Expand Down
File renamed without changes.
25 changes: 8 additions & 17 deletions packages/native/src/index.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,50 @@
import * as reactNative from 'react-native'
import { createCss } from '@emotion/primitives-core'

import { styled } from './styled'
import { styled } from './base'

const css = createCss(reactNative.StyleSheet)

const components = [
'ActivityIndicator',
'ActivityIndicatorIOS',
'ART',
'Button',
'DatePickerIOS',
'DrawerLayoutAndroid',
'FlatList',
'Image',
'ImageBackground',
'ImageEditor',
'ImageStore',
'KeyboardAvoidingView',
'ListView',
'MapView',
'Modal',
'NavigatorIOS',
'Picker',
'PickerIOS',
'ProgressBarAndroid',
'ProgressViewIOS',
'RecyclerViewBackedScrollView',
'RefreshControl',
'SafeAreaView',
'ScrollView',
'SectionList',
'SegmentedControlIOS',
'Slider',
'SliderIOS',
'SnapshotViewIOS',
'Switch',
'RecyclerViewBackedScrollView',
'RefreshControl',
'SafeAreaView',
'StatusBar',
'SwipeableListView',
'SwitchAndroid',
'Switch',
'SwitchIOS',
'TabBarIOS',
'Text',
'TextInput',
'ToastAndroid',
'ToolbarAndroid',
'Touchable',
'TouchableHighlight',
'TouchableNativeFeedback',
'TouchableOpacity',
'TouchableWithoutFeedback',
'View',
'ViewPagerAndroid',
'WebView',
'FlatList',
'SectionList',
'VirtualizedList'
'WebView'
]

export { css }
Expand Down
244 changes: 244 additions & 0 deletions packages/native/types/base.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// Definitions by: Pat Sissons <https://github.com/patsissons>
// TypeScript Version: 3.4

import {
ComponentPropsWithoutRef,
ComponentType,
NamedExoticComponent,
PropsWithChildren
} from 'react'
import { Theme } from '@emotion/core'
import * as RN from 'react-native'

type ReactNative = typeof RN

export type ReactNativeStyle = RN.ViewStyle | RN.TextStyle | RN.ImageStyle

export type ReactNativeComponentNames =
| 'ActivityIndicator'
| 'ActivityIndicatorIOS'
| 'Button'
| 'DatePickerIOS'
| 'DrawerLayoutAndroid'
| 'FlatList'
| 'Image'
| 'ImageBackground'
| 'KeyboardAvoidingView'
| 'ListView'
| 'Modal'
| 'NavigatorIOS'
| 'Picker'
| 'PickerIOS'
| 'ProgressBarAndroid'
| 'ProgressViewIOS'
| 'RecyclerViewBackedScrollView'
| 'RefreshControl'
| 'SafeAreaView'
| 'ScrollView'
| 'SectionList'
| 'SegmentedControlIOS'
| 'Slider'
| 'SnapshotViewIOS'
| 'StatusBar'
| 'SwipeableListView'
| 'Switch'
| 'SwitchIOS'
| 'TabBarIOS'
| 'Text'
| 'TextInput'
| 'ToolbarAndroid'
| 'TouchableHighlight'
| 'TouchableNativeFeedback'
| 'TouchableOpacity'
| 'TouchableWithoutFeedback'
| 'View'
| 'ViewPagerAndroid'
| 'WebView'

export type ReactNativeComponents = Pick<ReactNative, ReactNativeComponentNames>

export type ReactNativeComponentProps<
ComponentName extends ReactNativeComponentNames
> = ComponentPropsWithoutRef<ReactNativeComponents[ComponentName]>

export type ReactNativeStyleType<Props> = Props extends {
style?: RN.StyleProp<infer StyleType>
}
? StyleType extends ReactNativeStyle ? StyleType : ReactNativeStyle
: ReactNativeStyle

export type InterpolationPrimitive<
StyleType extends ReactNativeStyle = ReactNativeStyle
> =
| null
| undefined
| boolean
| number
| string
| ObjectInterpolation<StyleType>

export type ObjectInterpolation<
StyleType extends ReactNativeStyle = ReactNativeStyle
> = StyleType

export interface ArrayCSSInterpolation<
StyleType extends ReactNativeStyle = ReactNativeStyle
> extends Array<CSSInterpolation<StyleType>> {}

export type CSSInterpolation<
StyleType extends ReactNativeStyle = ReactNativeStyle
> = InterpolationPrimitive<StyleType> | ArrayCSSInterpolation<StyleType>

export interface ArrayInterpolation<
MergedProps,
StyleType extends ReactNativeStyle = ReactNativeStyle
> extends Array<Interpolation<MergedProps, StyleType>> {}

export interface FunctionInterpolation<
MergedProps,
StyleType extends ReactNativeStyle = ReactNativeStyle
> {
(mergedProps: MergedProps): Interpolation<MergedProps, StyleType>
}

export type Interpolation<
MergedProps = unknown,
StyleType extends ReactNativeStyle = ReactNativeStyle
> =
| InterpolationPrimitive<StyleType>
| ArrayInterpolation<MergedProps, StyleType>
| FunctionInterpolation<MergedProps, StyleType>

/** Same as StyledOptions but shouldForwardProp must be a type guard */
export interface FilteringStyledOptions<
Props,
ForwardedProps extends keyof Props = keyof Props
> {
shouldForwardProp?(propName: PropertyKey): propName is ForwardedProps
}

export interface StyledOptions<Props> {
shouldForwardProp?(propName: PropertyKey): boolean
}

/**
* @typeparam ComponentProps Props which will be included when withComponent is called
* @typeparam SpecificComponentProps Props which will *not* be included when withComponent is called
*/
export interface StyledComponent<
ComponentProps extends {},
SpecificComponentProps extends {} = {}
>
extends NamedExoticComponent<
PropsWithChildren<ComponentProps & SpecificComponentProps>
> {
withComponent<
Component extends ComponentType<ComponentPropsWithoutRef<Component>>
>(
component: Component
): StyledComponent<ComponentProps & ComponentPropsWithoutRef<Component>>
withComponent<ComponentName extends ReactNativeComponentNames>(
component: ReactNativeComponents[ComponentName]
): StyledComponent<ComponentProps, ReactNativeComponentProps<ComponentName>>
}

/**
* @typeparam ComponentProps Props which will be included when withComponent is called
* @typeparam SpecificComponentProps Props which will *not* be included when withComponent is called
*/
export interface CreateStyledComponent<
ComponentProps extends {},
SpecificComponentProps extends {} = {},
StyleType extends ReactNativeStyle = ReactNativeStyle
> {
/**
* @typeparam AdditionalProps Additional props to add to your styled component
*/
<AdditionalProps extends {} = {}>(
...styles: ArrayInterpolation<
ComponentProps &
SpecificComponentProps &
AdditionalProps & { theme: Theme },
StyleType
>
): StyledComponent<ComponentProps & AdditionalProps, SpecificComponentProps>
/**
* @typeparam AdditionalProps Additional props to add to your styled component
*/
<AdditionalProps extends {} = {}>(
template: TemplateStringsArray,
...styles: ArrayInterpolation<
ComponentProps &
SpecificComponentProps &
AdditionalProps & { theme: Theme },
StyleType
>
): StyledComponent<ComponentProps & AdditionalProps, SpecificComponentProps>
}

/**
* @desc
* This function accepts a React component.
*
* @example styled(MyComponent)({ width: 100 })
* @example styled(MyComponent)(myComponentProps => ({ width: myComponentProps.width })
* @example styled(View)({ width: 100 })
* @example styled(View)<Props>(props => ({ width: props.width })
*/
export interface CreateStyled {
<
Component extends ComponentType<ComponentPropsWithoutRef<Component>>,
ForwardedProps extends keyof ComponentPropsWithoutRef<
Component
> = keyof ComponentPropsWithoutRef<Component>
>(
component: Component,
options: FilteringStyledOptions<
ComponentPropsWithoutRef<Component>,
ForwardedProps
>
): CreateStyledComponent<
Pick<ComponentPropsWithoutRef<Component>, ForwardedProps> & {
theme?: Theme
},
{},
ReactNativeStyleType<ComponentPropsWithoutRef<Component>>
>

<Component extends ComponentType<ComponentPropsWithoutRef<Component>>>(
component: Component,
options?: StyledOptions<ComponentPropsWithoutRef<Component>>
): CreateStyledComponent<
ComponentPropsWithoutRef<Component> & { theme?: Theme },
{},
ReactNativeStyleType<ComponentPropsWithoutRef<Component>>
>

<
ComponentName extends ReactNativeComponentNames,
ForwardedProps extends keyof ReactNativeComponentProps<
ComponentName
> = keyof ReactNativeComponentProps<ComponentName>
>(
component: ReactNativeComponents[ComponentName],
options: FilteringStyledOptions<
ReactNativeComponentProps<ComponentName>,
ForwardedProps
>
): CreateStyledComponent<
{ theme?: Theme },
Pick<ReactNativeComponentProps<ComponentName>, ForwardedProps>,
ReactNativeStyleType<ReactNativeComponentProps<ComponentName>>
>

<ComponentName extends ReactNativeComponentNames>(
component: ReactNativeComponents[ComponentName],
options?: StyledOptions<ReactNativeComponentProps<ComponentName>>
): CreateStyledComponent<
{ theme?: Theme },
ReactNativeComponentProps<ComponentName>,
ReactNativeStyleType<ReactNativeComponentProps<ComponentName>>
>
}

export const styled: CreateStyled
54 changes: 54 additions & 0 deletions packages/native/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Definitions by: Pat Sissons <https://github.com/patsissons>
// TypeScript Version: 3.4

import { Theme } from '@emotion/core'

import {
CreateStyled as BaseCreateStyled,
CreateStyledComponent,
CSSInterpolation,
Interpolation,
ReactNativeStyle,
ReactNativeComponentNames,
ReactNativeComponentProps,
ReactNativeComponents,
ReactNativeStyleType
} from './base'

export {
ArrayCSSInterpolation,
ArrayInterpolation,
CreateStyledComponent,
CSSInterpolation,
FunctionInterpolation,
Interpolation,
InterpolationPrimitive,
ObjectInterpolation,
ReactNativeStyle,
StyledComponent,
StyledOptions
} from './base'

export function css<StyleType extends ReactNativeStyle = ReactNativeStyle>(
template: TemplateStringsArray,
...args: Array<Interpolation>
): StyleType
export function css<StyleType extends ReactNativeStyle = ReactNativeStyle>(
...args: Array<StyleType>
): StyleType
export function css<StyleType extends ReactNativeStyle = ReactNativeStyle>(
...args: Array<CSSInterpolation>
): StyleType

export type StyledComponents = {
[ComponentName in ReactNativeComponentNames]: CreateStyledComponent<
{ theme?: Theme },
ReactNativeComponentProps<ComponentName>,
ReactNativeStyleType<ReactNativeComponentProps<ComponentName>>
>
}

export interface CreateStyled extends BaseCreateStyled, StyledComponents {}

declare const styled: CreateStyled
export default styled
Loading

0 comments on commit 456be9a

Please sign in to comment.