Emotion: Typescript definitions for @emotion/native

Created on 6 Sep 2018  路  29Comments  路  Source: emotion-js/emotion

Hey,
I'm building a RN app and wish to use @emotion/native, but since I'm using TS I'm having problems declaring correct typings.

React Native beginner friendly

Most helpful comment

A PR implementing this ( https://github.com/emotion-js/emotion/pull/1634 ) has just been merged into next branch. This will be a part of the upcoming v11 release.

All 29 comments

I can take look into it - is there anything before I jump into declaring types?

@krzysztofzuraw Are you still on to this? I'm interested in this too.

I'd be happy to help with this. (Though I've never done anything like it before.)

I was playing around with emotion in react-native today, and ended up writing some brief but fairly accurate typings for the native package. I'm not confident i will have time to author a PR for this any time soon, so i'll post the effort on this issue in case it helps anyone else. Note that I added @emotion/core and @emotion/styled-base to devDependencies so i can reuse their existing type definitions.

The strategy here was to reuse as much existing typing as i could. This was easy for the css export since it's effectively the same for react-native. The styled export is a bit more complicated. I took the list of bound components and turned it into a giant string literal union. Then i created a new mapped type for mapping component names into styled components and joined that with the CreateStyled types from styled-base. This should cover nearly every use case for react-native as far as i can tell.

// global.d.ts

declare module '@emotion/native' {
  import css from '@emotion/css';
  import {
    CreateStyled,
    CreateStyledComponentExtrinsic,
  } from '@emotion/styled-base';
  import React from 'react';
  import ReactNative from 'react-native';

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

  type StyledComponentsForReactNative<
    T extends keyof typeof ReactNative,
    ExtraProps,
    Theme
  > = {
    [K in T]: CreateStyledComponentExtrinsic<
      typeof ReactNative[K],
      ExtraProps,
      Theme
    >;
  };

  export interface Styled<Theme extends object = any, ExtraProps = {}>
    extends CreateStyled<Theme>,
      StyledComponentsForReactNative<
        StyledReactNativeComponents,
        ExtraProps,
        Theme
      > {}

  export {css};

  const styled: Styled;
  export default styled;
}

_updated with the changes below_

@patsissons thanks, that did fixed the issue of styled.* not autocompleting. Do you have any idea on how to make it work with theming? (this is the solution when working with react web).

I tried doing this but did not had success:

import styled, { Styled } from '@emotion/native'
import { Theme } from '../config/types'

export default styled as Styled<Theme>

I'll have a look tomorrow

Thanks @patsissons. Any suggestion?

sorry for the delay. here are some updates to fix the issue (plus allow for ExtraProps support).

  type StyledComponentsForReactNative<
    T extends keyof typeof ReactNative,
    ExtraProps,
    Theme
  > = {
    [K in T]: CreateStyledComponentExtrinsic<
      typeof ReactNative[K],
      ExtraProps,
      Theme
    >;
  };

  export interface Styled<Theme extends object = any, ExtraProps = {}>
    extends CreateStyled<Theme>,
      StyledComponentsForReactNative<
        StyledReactNativeComponents,
        ExtraProps,
        Theme
      > {}

short demo of it working

import styledNative, {Styled} from '@emotion/native';

type Theme = {
  color: {
    primary: string;
    positive: string;
    negative: string;
  };
};

const styled = styledNative as Styled<Theme, {foo: string}>;

export const PositiveView = styled.View`
  background-color: ${({theme}) => theme.color.positive}; // ${({foo}) => foo}
`;

Works awesome man! Thanks for helping.

hey guys, how are you adding the types to your project? I keep getting this error:

Invalid module name in augmentation. Module '@emotion/native' resolves to an untyped module at '...mypath.../node_modules/@emotion/native/dist/native.cjs.js', which cannot be augmented.

I'm adding them in src/typings/global.d.ts.

Thanks!

never mind, my issue was that I'm using a monorepo and ran into some problems with emotion getting hoisted. Had to add @emotion to the nohoist option.

Ok, so that didn't solve my actual problem with adding the custom definitions...馃様

Hi @patsissons, I want to thank you for your definition.

After adding your code to my codebase, I face a problem. Can you help me to debug it?

Screen Shot 2019-09-12 at 10 44 31 PM

@patsissons, how would you handle with

SerializedStyles' is not assignable to type 'StyleProp'.

when using css on custom style attributes like contentContainerStyle={css``}, slideStyle

@damathryx not much you can do here, the two types are not compatible as defined and there is no real way to infer structure from the seralized form. you can either type cast down to {} or you can redefine the css export to return {} (if you never care about serialized properties). For the latter strategy you can replace export {css} with

export function css(
  template: TemplateStringsArray,
  ...args: Array<Interpolation>
): {};
export function css(...args: Array<Interpolation>): {};

Alternatively, I have been using this utility and type construct to share both types.

// utilities.ts
import {Interpolation, SerializedStyles} from '@emotion/serialize';
import {css} from '@emotion/native';

type EmotionStyles = Record<string, SerializedStyles>;

export type ReactNativeStyles<T extends EmotionStyles> = {
  [K in keyof T]: {};
};

export function cssRN(
  template: TemplateStringsArray,
  ...args: Array<Interpolation>
): {};
export function cssRN(...args: Array<Interpolation>): {};
export function cssRN(...args: any[]) {
  return css(args);
}

export function asReactNativeStyles<T extends EmotionStyles>(
  serialized: T,
): ReactNativeStyles<T> & {serialized: EmotionStyles} {
  return {
    serialized,
    ...serialized,
  };
}

now you can access the serialized styles if you need to, and otherwise just use styles.container for example when applying to a react native style prop.

import {css} from '@emotion/native';
import {asReactNativeStyles} from './utilities';

export const styles = asReactNativeStyles({
  container: css`
    flex: 1;
  `,
  centered: css`
    justify-content: center;
    align-items: center;
  `,
  // ...
});

cssRN is just a handy helper to do inline css expansions

@patsissons would you maybe be interested in helping to prepare a PR with official typings for those packages based on what we currently have on the next branch?

@Andarist i created a branch and added some (cleaned up) types to it. also turns out that all of that css stuff is unnecessary since under the hood react-native calls to css just invoke Stylesheet.flatten so i've adjusted the types to reflect that. Shall i fork or contribute directly?

for those not on the next release, here is the updated css type.

  export function css(
    template: TemplateStringsArray,
    ...args: Array<Interpolation>
  ): ReturnType<typeof StyleSheet.flatten>;
  export function css(
    ...args: Array<Interpolation>
  ): ReturnType<typeof StyleSheet.flatten>;

@imcvampire i cleaned up the types that i had originally posted with a better mapping pattern, this may solve or at least give you more context why there is an error. Below are the types that i have changed.

 type StyledReactNativeComponents = Pick<
    typeof ReactNative,
    | 'ActivityIndicator'
    | 'ActivityIndicatorIOS'
    | 'ART'
    | 'Button'
    | 'DatePickerIOS'
    | 'DrawerLayoutAndroid'
    | 'Image'
    | 'ImageBackground'
    | 'ImageEditor'
    | 'ImageStore'
    | 'KeyboardAvoidingView'
    | 'ListView'
    // does not exist?
    // | 'MapView'
    | 'Modal'
    | 'NavigatorIOS'
    | 'Picker'
    | 'PickerIOS'
    | 'ProgressBarAndroid'
    | 'ProgressViewIOS'
    | 'ScrollView'
    | 'SegmentedControlIOS'
    | 'Slider'
    // type alias to Slider
    // | 'SliderIOS'
    | 'SnapshotViewIOS'
    | 'Switch'
    | 'RecyclerViewBackedScrollView'
    | 'RefreshControl'
    | 'SafeAreaView'
    | 'StatusBar'
    | 'SwipeableListView'
    // does not exist?
    // | 'SwitchAndroid'
    | 'SwitchIOS'
    | 'TabBarIOS'
    | 'Text'
    | 'TextInput'
    | 'ToastAndroid'
    | 'ToolbarAndroid'
    // just an interface
    // | 'Touchable'
    | 'TouchableHighlight'
    | 'TouchableNativeFeedback'
    | 'TouchableOpacity'
    | 'TouchableWithoutFeedback'
    | 'View'
    | 'ViewPagerAndroid'
    | 'WebView'
    | 'FlatList'
    | 'SectionList'
    // migrated to FlatList?
    // | 'VirtualizedList'
  >;

  type ReactNativeStyledComponents<
    Theme extends object = any,
    ExtraProps = {}
  > = {
    [K in keyof StyledReactNativeComponents]: typeof ReactNative[K] extends ComponentType
      ? CreateStyledComponentExtrinsic<typeof ReactNative[K], ExtraProps, Theme>
      : never;
  };

  export interface Styled<Theme extends object = any, ExtraProps = {}>
    extends CreateStyled<Theme>,
      ReactNativeStyledComponents<Theme, ExtraProps> {}

@patsissons Thank you! It works great for me!

One more request, do you have any correct typing for css function?

Thank you!

@Andarist i created a branch and added some (cleaned up) types to it. also turns out that all of that css stuff is unnecessary since under the hood react-native calls to css just invoke Stylesheet.flatten so i've adjusted the types to reflect that. Shall i fork or contribute directly?

That would be sweet - just fork from next branch and send us a PR. We'd love to get this merged in. The types should be written fairly in the same manner as refactored typings for @emotion/styled (available on next branch) - this should make maintenance and interoperability easier.

A PR implementing this ( https://github.com/emotion-js/emotion/pull/1634 ) has just been merged into next branch. This will be a part of the upcoming v11 release.

Nice job on adding this 馃帀 is there an estimated date for a stable release of v11?

Yes I'd love to hear about 11. Might just switch to it now for TS support? expo install @emotion/core@next @emotion/native@next ?

We hope to release v11 in May - there are no planned changes for native packages, so I would say that it's rather safe to use the next release line already. Just pin your versions and you should be able to use it without any problems.

Any movement on this? Type safety on the css prop in react native would be an utter game changer

@samburgers those types are part of the v11 that is already released to npm. I'm going to release first RC version today - as the work on v11 has been, finally, completed.

@Andarist thank you so much for your hard work! :muscle:

How to extend the Theme with this new version? Just like in here
https://github.com/emotion-js/emotion/issues/839#issuecomment-553719116

Was this page helpful?
0 / 5 - 0 ratings

Related issues

desmap picture desmap  路  3Comments

smlmrkhlms picture smlmrkhlms  路  3Comments

artooras picture artooras  路  3Comments

meebix picture meebix  路  3Comments

mitchellhamilton picture mitchellhamilton  路  3Comments