Skip to content
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

Using TypeScript x React Native Web #832

Closed
jaredpalmer opened this issue Feb 21, 2018 · 35 comments
Closed

Using TypeScript x React Native Web #832

jaredpalmer opened this issue Feb 21, 2018 · 35 comments

Comments

@jaredpalmer
Copy link

For those that are interested, this is how I am monkey patching @types/react-native to work with React Native Web. This postinstall script fixes the react-native / node conflict and adds my RN-Web -specific types to @types/react-native without augmenting any of the core @types/react-native typings. This is because TS will effectively combine interface declarations with the same name. My typings are as of 0.4.0, but should work with 0.5.0. The only thing that is a little hand-wavy is that TextStyle has a resize?: string that only actually applies to TextInput. This is documented in the comments and should thus show up in VSCode autocomplete. Hope this helps anyone else.

@necolas lmk if you'd be willing to accept a PR for either docs and/or for the typings. Could be nice to add a react-native-web/ts-setup.js that runs the script below.

// ./package.json
{
  "scripts": {
    "postinstall": "node ./postinstall.js"
  }
}
// ./postinstall.js
'use strict';

const fs = require('fs');

const RN_TSD = __dirname + '/node_modules/@types/react-native/index.d.ts';
const raw = fs.readFileSync(RN_TSD);

// Fix @types/node conflict
// @see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/15960
// @see https://gist.github.com/rawrmaan/be47e71bd0df3f7493ead6cefd6b400c
fs.writeFileSync(
  RN_TSD,
  raw.toString().replace('declare global', 'declare namespace RemovedGlobals')
);

// Add React Native Web Types to @types/react-native.
if (!raw.includes('REACT-NATIVE-WEB TYPINGS')) {
  fs.writeFileSync(
    RN_TSD,
    raw.toString().replace(
      `dismiss: () => void;\n}`,
      `dismiss: () => void;\n}
      //////////////////////////////////////////////////////////////////////////
      //
      //  REACT-NATIVE-WEB TYPINGS
      //
      //////////////////////////////////////////////////////////////////////////
      export interface ImageProperties {
          /** 
           * When false, the image will not be draggable 
           * @platform web
           */
          draggable?: boolean;
      }
      
      export interface TextInputProperties {
          /** 
           * Indicates whether the value of the control can be automatically completed by the browser
           * @platform web
           */
          autoComplete?: string;
      }
      
      export interface SwitchProperties {
          /** 
           * The color of the thumb grip when the switch is turned on. 
           * @platform web
           */
          activeThumbColor?: string;
          /** 
           * The color of the track when the switch is turned on.  
           * @platform web
           */
          activeTrackColor?: string;
          /** 
           * The color of the thumb grip when the switch is turned off. 
           * @platform web
           */
          thumbColor?: string;
          /** 
           * The color of the track when the switch is turned off.
           * @platform web
           */
          trackColor?: string;
      }
      
      export interface TextStyle {
          /** @platform web */
          fontFeatureSettings?: string;
          /** @platform web */
          textIndent?: string;
          /** @platform web */
          textOverflow?: string;
          /** @platform web */
          textRendering?: string;
          /** @platform web */
          textTransform?: string;
          /** @platform web */
          unicodeBidi?: string;
          /** @platform web */
          wordWrap?: string;
          /** 
           * TextInput only! 
           * @platform web 
           */
          resize?: string;
      }
      
      export interface ViewStyle {
          /** @platform web */
          animationDelay?: string;
          /** @platform web */
          animationDirection?: string;
          /** @platform web */
          animationDuration?: string;
          /** @platform web */
          animationFillMode?: string;
          /** @platform web */
          animationName?: string | Array<Object>;
          /** @platform web */
          animationIterationCount?: number | "infinite";
          /** @platform web */
          animationPlayState?: string;
          /** @platform web */
          animationTimingFunction?: string;
          /** @platform web */
          backgroundAttachment?: string;
          /** @platform web */
          backgroundBlendMode?: string;
          /** @platform web */
          backgroundClip?: string;
          /** @platform web */
          backgroundImage?: string;
          /** @platform web */
          backgroundOrigin?: string;
          /** @platform web */
          backgroundPosition?: string;
          /** @platform web */
          backgroundRepeat?: string;
          /** @platform web */
          backgroundSize?: string;
          /** @platform web */
          boxShadow?: string;
          /** @platform web */
          boxSizing?: string;
          /** @platform web */
          clip?: string;
          /** @platform web */
          cursor?: string;
          /** @platform web */
          filter?: string;
          /** @platform web */
          gridAutoColumns?: string;
          /** @platform web */
          gridAutoFlow?: string;
          /** @platform web */
          gridAutoRows?: string;
          /** @platform web */
          gridColumnEnd?: string;
          /** @platform web */
          gridColumnGap?: string;
          /** @platform web */
          gridColumnStart?: string;
          /** @platform web */
          gridRowEnd?: string;
          /** @platform web */
          gridRowGap?: string;
          /** @platform web */
          gridRowStart?: string;
          /** @platform web */
          gridTemplateColumns?: string;
          /** @platform web */
          gridTemplateRows?: string;
          /** @platform web */
          gridTemplateAreas?: string;
          /** @platform web */
          outline?: string;
          /** @platform web */
          outlineColor?: string;
          /** @platform web */
          overflowX?: string;
          /** @platform web */
          overflowY?: string;
          /** @platform web */
          overscrollBehavior?: "auto" | "contain" | "none";
          /** @platform web */
          overscrollBehaviorX?: "auto" | "contain" | "none";
          /** @platform web */
          overscrollBehaviorY?: "auto" | "contain" | "none";
          /** @platform web */
          perspective?: string;
          /** @platform web */
          perspectiveOrigin?: string;
          /** @platform web */
          touchAction?: string;
          /** @platform web */
          transformOrigin?: string;
          /** @platform web */
          transitionDelay?: string;
          /** @platform web */
          transitionDuration?: string;
          /** @platform web */
          transitionProperty?: string;
          /** @platform web */
          transitionTimingFunction?: string;
          /** @platform web */
          userSelect?: string
          /** @platform web */
          visibility?: string;
          /** @platform web */
          willChange?: string;
      }
      
      export interface TextProperties {
          /** 
           * Allows assistive technologies to present and support interaction with the view in a manner that is consistent with user expectations for similar views of that type. For example, marking a touchable view with an accessibilityRole of button. For compatibility with React Native accessibilityTraits and accessibilityComponentType are mapped to accessibilityRole. (This is implemented using ARIA roles.) 
           * @platform web
           */
          accessibilityRole?: 'button' | 'heading' | 'label' | 'link' | 'listitem';
      }
      
      
      export interface CheckBoxProps extends ViewProperties {
          /** 
           * Invoked with the event when the value changes. 
           * @platform web
           */
          onChange?: Function;
          /** 
           * Invoked with the new value when the value changes. 
           * @platform web
           */
          onValueChange?: Function;
          /** 
           * The value of the checkbox. If \`true\` the checkbox will be checked. 
           * @platform web
           */
          value?: boolean;
          /** 
           * If true, the user won't be able to interact with the checkbox. 
           * @platform web
           */
          disabled?: boolean;
          /** 
           * Customize the color of the checkbox.
           * @platform web  
           */
          color?: string;
      }
      
      export interface CheckBoxStatic extends React.ComponentClass<CheckBoxProps> {}
      export type CheckBox = CheckBoxStatic;
  `
    )
  );
}
@necolas
Copy link
Owner

necolas commented Feb 24, 2018

I'm not familiar with TypeScript but probably don't want to maintain types for it (as RN doesn't either). Is this something that can be added to @types/react-native?

@kristerkari
Copy link
Contributor

Adding that script to automatically run on react-native-web post install is not that good idea. It could easily break things if things change in @types/react-native.

@jaredpalmer Why don't you just create a repository that has the script and instructions on how to use it? There could be a mention about the script in react-native-web docs. That way anyone who needs it can add it to their project.

@jaredpalmer
Copy link
Author

@kristerkari I think that is the best way forward. Imho the script is about as fragile as the typings themselves, but I get your point.

@necolas
Copy link
Owner

necolas commented Feb 26, 2018

Happy to include something in the docs as a start

Repository owner deleted a comment from foltz Mar 5, 2018
@nickdima
Copy link

@jaredpalmer this looks great! Any chance you will publish this?

@jaredpalmer
Copy link
Author

I’ve updated this a little since posting. There are significant differences related to strings vs. numbers in the types. Will try to publish soon

@nickdima
Copy link

@jaredpalmer that's great to hear.
One type annotation that I noticed was missing is the one for the function AppRegistry.getApplication.

@jaredpalmer
Copy link
Author

Yeah it’s incomplete for sure.

@teebot
Copy link

teebot commented Mar 28, 2018

@jaredpalmer I was wondering. If we had a @types/react-native-web package maybe we could find a way to alias @types/react-native to that? I mean the same way webpack aliases react-native as react-native-web.

@kristerkari
Copy link
Contributor

@teebot yeah it is possible to do this in package.json:

"@types/react-native": "mygithubusername/myrepo",

@nickdima
Copy link

I'm a newbie at TypeScript so this may sound stupid, but: would it be possibile to have @types/react-native-web "globally" extend @types/react-native and only add the stuff that is missing?

@necolas
Copy link
Owner

necolas commented Mar 29, 2018

We can add something to the docs once y'all figure this out. Thanks :)

@rakannimer
Copy link

rakannimer commented Apr 16, 2018

I'm currently creating a @types/react-native-web alias to @types/react-native using a yarn/npm hack :

yarn add -D @types/react-native-web@npm:@types/react-native

npm :

npm i -D @types/react-native-web@npm:@types/react-native

Not the best way, but it works(-ish).

@ethanneff
Copy link

ethanneff commented May 23, 2018

If anyone else finds this thread, I made a boilerplate for react-native-web with typescript: https://github.com/ethanneff/react-native-web-typescript

@AleksandarAleksandrov
Copy link

@ethanneff Thank you! Works like a charm :)

@lukepighetti
Copy link

Thank you @ethanneff will give it a try. In the mean time, does ‘react-native-web’ team recommend any particular type system?

@steida
Copy link

steida commented Dec 9, 2018

@types/react-native-web does not exists. @ethanneff does not solve the problem with React Native Web custom types.

Because there are no TypeScript types for React Native Web, my workaround is wrappers with types added as we go.

import React from 'react';
import { Text, TextProps } from 'react-native';

// Add React Native Web custom props.
type AppTextProps = TextProps & {
  accessibilityRole: 'link';
  onMouseEnter: () => void;
  onMouseLeave: () => void;
};

const AppText: React.FunctionComponent<AppTextProps> = props => {
  return <Text {...props} />;
};

export default AppText;

@steida
Copy link

steida commented Dec 9, 2018

Hmm, but that's not correct for universal components. This is the better and spread operator is not type checked. It's an interesting thing to think by the way. How to properly type different platforms? I believe it should be possible with TypeScript conditional types. Aka, accessibilityRole: 'link' only in web platform etc. Meanwhile, I use this:

<Text
  {...Platform.select({
    web: {
      accessibilityRole: 'link',
      onMouseEnter: () => setIsActive(true),
      onMouseLeave: () => setIsActive(false),
    },
  })}
  style={[
    style || theme.link,
    (isActive || routeIsActive()) && (activeStyle || theme.linkActive),
  ]}
>
  {children}
</Text>

@slorber
Copy link
Contributor

slorber commented Mar 18, 2019

Also looking for a decent option for using RNW with TS

@steida Instead of wrapping in new FC, why not just playing with types and adding the link?

type TextProps = React.ComponentProps<typeof Text>
type WebTextProps = TextProps & {
  href?: string
}
const FixedText = Text as ComponentType<WebTextProps>

@regalstreak
Copy link

Any updates on typescript support for RNW or is aliasing still the way for types?

@mikeaustin
Copy link

What's the current supported solution to use Create React App with TypeScript and React Native for Web? I just went to start a new project and realized it doesn't work out of the box.

@BrianLi101
Copy link

My team has been using react-native-web and ended up creating a repo with the type conversions from Flow to TypeScript. We haven't completed translations for all of the types just yet but have covered most of the highly used UI components. Hopefully, this helps!

https://github.com/gtechnologies/react-native-web-ts-types

@nandorojo
Copy link
Contributor

I successfully added TS support to React Native Web with a single file. Declaration merging handles this for you.

You can see my solution here: #1684 (comment)

@intergalacticspacehighway
Copy link
Contributor

@nandorojo Can you share the file If possible?
I'd love to contribute with recent 0.15 accessibility changes in RN Web. Also, I need it for one of my projects.

@nandorojo
Copy link
Contributor

I don't have a fully-made file yet unfortunately, I've just been going prop-by-prop as shown in my linked comment. It would be awesome to have this, though.

@intergalacticspacehighway
Copy link
Contributor

Cool, no worries. Looks like a bit of work for one person 😅. Should we have a temp repo for this, till we figure out something? A single file will make it painful to update though.

@sstur
Copy link

sstur commented Jan 26, 2022

I successfully added TS support to React Native Web with a single file.

@nandorojo, would you be keen to share your d.ts file that uses declaration merging. There's still nothing on @types/react-native-web and I don't want to have to augment all the interfaces by hand if folks in the community have done this already. Thanks in advance!

Edit: this seems to have some level of type coverage, but it's not using the approach you mentioned and I'm a bit skeptical that it's up to date with RNW.

@nandorojo
Copy link
Contributor

nandorojo commented Jan 26, 2022

here you go:

// react-native-web/overrides.ts
declare module 'react-native' {
  interface PressableStateCallbackType {
    hovered?: boolean
    focused?: boolean
  }
  interface ViewStyle {
    transitionProperty?: string
    transitionDuration?: string
  }
  interface TextProps {
    accessibilityComponentType?: never
    accessibilityTraits?: never
    href?: string
    hrefAttrs?: {
      rel: 'noreferrer'
      target?: '_blank'
    }
  }
  interface ViewProps {
    accessibilityRole?: string
    href?: string
    hrefAttrs?: {
      rel: 'noreferrer'
      target?: '_blank'
    }
    onClick?: (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void
  }
}

@sstur
Copy link

sstur commented Feb 1, 2022

Thanks @nandorojo for getting me started here! I added a bunch more types that are relevant to my codebase, in case anyone else finds these helpful, I've dropped it into a Gist here:
https://gist.github.com/sstur/9693aa41e95078c8c6bcf3d8f1216872

@mikehardy
Copy link

mikehardy commented Jun 9, 2022

@nandorojo looks like 'ViewStyle' needs a cursor prop ;-)
#1847 (comment)
Nice solution on the overrides types file, would be excellent to see it mainlined here

@sstur I do not see an import 'react-native' in your overrides, how do you combine the base types and your overrides?

@sstur
Copy link

sstur commented Jun 10, 2022

I don't remember, I think it just works without that.

@Frexuz
Copy link

Frexuz commented Aug 29, 2022

AppRegistry.getApplication still missing

@bryanprimus
Copy link

here you go:

// react-native-web/overrides.ts
declare module 'react-native' {
  interface PressableStateCallbackType {
    hovered?: boolean
    focused?: boolean
  }
  interface ViewStyle {
    transitionProperty?: string
    transitionDuration?: string
  }
  interface TextProps {
    accessibilityComponentType?: never
    accessibilityTraits?: never
    href?: string
    hrefAttrs?: {
      rel: 'noreferrer'
      target?: '_blank'
    }
  }
  interface ViewProps {
    accessibilityRole?: string
    href?: string
    hrefAttrs?: {
      rel: 'noreferrer'
      target?: '_blank'
    }
    onClick?: (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void
  }
}

do we still need to do this?

@blazejkustra
Copy link

Happy to include something in the docs as a start

We can add something to the docs once y'all figure this out. Thanks :)

Types for react-native-web are now available on DefinitelyTyped.

@necolas Are you still open to the idea of making a mention in the docs?

@necolas
Copy link
Owner

necolas commented Aug 8, 2024

Yeah sounds good. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests