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

Exporting types #1684

Closed
necolas opened this issue Jul 24, 2020 · 35 comments
Closed

Exporting types #1684

necolas opened this issue Jul 24, 2020 · 35 comments
Milestone

Comments

@necolas
Copy link
Owner

necolas commented Jul 24, 2020

I'm looking for input from people who use typed JavaScript with this library.

  1. If you use Flow or TypeScript, how do you type react-native-web exports?
  2. If you work across platforms, how do you deal with platform-specific differences in the APIs or components like View?
  3. Does this library need to export Flow and TypeScript types?
  4. Is there a way to generate TypeScript interfaces from Flow types so you don't need to maintain both?
@EvanBacon
Copy link
Contributor

Here is a useful dialog about TypeScript in RNW: #832

There is no DefinitelyTyped package for react-native-web. I'm not entirely sure how one would work (efficiently). I've seen people propose copying all react-native types and then extending them, and somehow aliasing types/react-native to types/react-native-web. This of course wouldn't work with other out-of-tree solutions so I would discourage the approach.

Personally I would opt for universal packages like "react animated" or "react text" (probably not with these names since they're all taken). These universal packages would implement iOS, Android, and web in the same place. Said approach would also fix some versioning issues and help reduce the custom bundler configuration required for universal React apps.

I will note that RN is now moving towards a monorepo structure (creating packages like react-native/normalize-color color and react-native/polyfills) which could make modifications like this more feasible.

At Expo, we've refactored all of our universal React packages from flow to TypeScript (as opposed to adding external type definitions files .d.ts) because we find this is easier to work with from a DX perspective.

I personally think that having better types will help bring more visibility to important react-native-web features (e.g. href on Text). Due to the high demand in the Expo/React community for TypeScript support on web, I may look at creating some abstract packages which reexport react-native(-web) modules with proper types.

@paularmstrong
Copy link
Contributor

paularmstrong commented Jul 25, 2020

In Build Tracker (typescript, web-only), I pulled the react-native types from definitely-typed, then removed all of the iOS/Android specific stuff and added web-specific bits (source). Ideally I would take the few minutes to contribute this back, but I don't feel comfortable enough with my knowledge on Typescript to really be authoritative that any of it was done correctly.

At Twitter, we have our own flow definitions for react-native-web, but had built this up before this package was exporting most of its types. Ideally, we would love to use the source here, but have found that our type definitions tend to be more strict (which we like).

Is there a way to generate TypeScript interfaces from Flow types so you don't need to maintain both?

There is an inverse for this using flow-typed, though it's not 100% working. I've found it work for maybe 75% of the times I've used it. The times it didn't, some light hand-editing fixed it.

@necolas
Copy link
Owner Author

necolas commented Jul 26, 2020

@EvanBacon I don't know that much of what you suggested is actionable. I'm a bit concerned about the implication that you're going to repackage everything under different packages

@paularmstrong I wasn't aware this package was exporting types at all 😅 Would be happy to hear about what your flow types look like and what stricter types could be upstreamed

@paularmstrong
Copy link
Contributor

Ah, they're not exporting types, correct. I believe I had done something with the module.name_mapper to re-map to the src files. It worked out okay, but not everything was fully typed at the time. And the main index.js not having // @flow at the top was problematic as well.

I'm not as connected with that area of our code anymore, so I'm not certain what we have that's more strict. Maybe @comp615 has some thoughts on that.

The one thing that I've noticed that's really nice about the TypeScript types from react-native that I'm using in Build Tracker is that the StyleSheet props are fully typed and throws errors if you use a style that isn't allowed (like color on a View doesn't work, but will on Text). We definitely don't have that same level of strict typing in Flow.

@comp615
Copy link
Contributor

comp615 commented Jul 28, 2020

I filed a ticket during the last update (pre 0.13) to look into using the RNW types by default (with Paul's mapper hack) instead of our typedef module file. As @paularmstrong mentioned, they are not currently exported in RNW, which would be a great (and I think easy?) starting point to alleviate some of the boilerplate.

In particular, it would really help with major updates since it makes it much more trivial to track down all the things that changed and have certainty around them since they'd rely on the real source definition and not our manual list. I found it very useful last upgrade to read the release notes and update our flow file accordingly, and then go fix each usage in our code where there was a flow error.

@necolas
Copy link
Owner Author

necolas commented Jul 29, 2020

What's the best way to export the types? Babel is currently stripping them when it compiles the files.

@paularmstrong
Copy link
Contributor

flow-copy-source or DIY copy every file over to your dist directory and add .flow as the final extension. Probably also need to ensure // @flow is at the top of every file that supports types

@Albert-Gao
Copy link

Albert-Gao commented Aug 10, 2020

Environment:

  • expo SDK v38 (managed workflow)
  • "@types/react-native": "~0.63.6"
  • "react-native-web": "~0.13.5"
  • "typescript": "~3.9.7"
  • "react": "0.0.0-experimental-4c8c98ab9"
  • "react-dom": "0.0.0-experimental-4c8c98ab9"

I do not use any new API from RN v0.63, so so far no problem.

For my project, for any cross-platform components,

two cases:

  1. the native and web share the same signature.
    • I create the components.tsx and components.web.tsx, and always use components.tsx as the source when importing.
  2. the native and web share a different signature.
    • I create a single entry like component.tsx and deal with the platform difference from there, like the above and platform check.

And all the type info, I just use from @types/react-native

No problems and works great.

So, I always share the same signature across platforms. Because I do not want to propagate the platform differences to the caller, so the vast majority of the codebase can be platform agnostic.

In short,

I think if react-native-web can maintain an exact same type info match to @types/react-native, that will be awesome, but if there are any differences, just need to doc it, and I am fine with that, that just means, for any differences, we might need to create this one more abstraction (for example different file extension and implementation for a different platform) for every difference that we are using in the user-land code, but that is totally understandable.

But if the API surface can be 100% percent compatible (for non-supported, won't throw an error), then it should be fine to just use the @types/react-native, otherwise, seems unnecessary and maintaining the types are a big task

thanks for the awesome package, so beautiful.

@necolas
Copy link
Owner Author

necolas commented Sep 3, 2020

I came here because I was shocked to find out that there's no Typescript types

Well, RN is Flow typed and doesn't provide TS types, so I don't know what you're pretending to be so shocked about.

I would advice leveraging on @types/react-native instead

Those types aren't accurate for Windows or Web.

@es-lynn
Copy link

es-lynn commented Sep 3, 2020

Well, RN is Flow typed and doesn't provide TS types, so I don't know what you're pretending to be so shocked about.

Most RN libraries provide types.

Those types aren't accurate for Windows or Web.

Can you give examples of which parts aren't accurate?

Offhand I can only think of ScrollView: { bounces: boolean } , which itself isn't even accurate for mobile because it's not supported on Android.

@JavanPoirier
Copy link

JavanPoirier commented Oct 11, 2020

I'm not entirely sure how one would work (efficiently). I've seen people propose copying all react-native types and then extending them, and somehow aliasing types/react-native to types/react-native-web. This of course wouldn't work with other out-of-tree solutions so I would discourage the approach.

@EvanBacon, At You.i TV we have a out of tree RN solution to extend to 11+ platforms and I did just that for our type definitions. I extend from the official RN types and Omit/Pick the types as needed. You mention, "This of course wouldn't work with other out-of-tree solutions so I would discourage the approach." I fail to see how this is the case. Can you please explain?

As omitting/picking the types is a job of its own, for now I am happy to have all RN types for RN Web vs none. So I just pointed the RN types to it with: yarn add -D @types/react-native-web@npm:@types/react-native

You can also just install @types/react-native and point the type package to react-native-web through your tsconfig.js:

{
  "compilerOptions": {
    ...
    "baseUrl": "./", // Required with the use of paths. May differ depending on project structure.
    "paths": {
      "@types/react-native-web": ["./node_modules/@types/react-native"]
    },
    ...
  }
}

@JavanPoirier
Copy link

JavanPoirier commented Oct 11, 2020

@necolas, I could look into writing the initial TypeScript definition file for this library. I see that the Flow types are include in the repo. I am not experienced with Flow, however would you like to the types to be included here (like Flow) or in Definitely Typed?

@nandorojo
Copy link
Contributor

nandorojo commented Oct 19, 2020

Could TypeScript declaration merging solve this?

That's what I'm using in my current project.

Example

// react-native-web/overrides.ts

declare module 'react-native' {
  interface PressableStateCallbackType {
    hovered: boolean
  }
}

That would extend the existing PressableStateCallbackType from @types/react-native (with one caveat, more on that below.)

Screen Shot 2020-10-19 at 12 32 15 PM

Caveat

There is one problem with this approach at the moment. @types/react-native exports certain types as type rather than interface. This prevents declaration merging.

For instance, this is the current type for Pressable's callback:

// @types/react-native/index.d.ts line 473
export type PressableStateCallbackType = Readonly<{
    pressed: boolean;
}>;

Changing it to this makes it extensible:

export interface PressableStateCallbackType {
    pressed: boolean;
}

Thus, declaration merging could (in a few cases) require a PR to @types/react-native turning type into interface.

@hosseinmd
Copy link
Contributor

I think ReactNative could have web type too. ReactNative supported specific type for android or iOS that could support specific type for web too.

@RichardLindhout
Copy link
Contributor

I don't think React Native wants that as the react-native-macos, react-native-web, react-native-windows are not 'official'. Maybe it would be better to create a different type system which has special properties for all these platforms available, but that would require a lot of work.

Maybe we will have to wait for React Native to add iOS Pointer support as this would probably create the hovered in React Native itself for iOS:
https://react-native.canny.io/feature-requests/p/ios-pointer-support

@hosseinmd
Copy link
Contributor

I do not agree with you because React Native added web to official documentation but not added react-native-macos or react-native-windows

@necolas necolas added this to the 0.15 milestone Oct 27, 2020
@minheq
Copy link

minheq commented Nov 10, 2020

If you use Flow or TypeScript, how do you type react-native-web exports?
I use @types/react-native. When there are missing types for props that are supported on react-native-web I wrap the component with right types for props, and pass though to react-native component. But I like @nandorojo approach as well.

If you work across platforms, how do you deal with platform-specific differences in the APIs or components like View?
Same as above, but I will add comments to the prop type.

Does this library need to export Flow and TypeScript types?
I don't think so. I would rely on @types/react-native only, and patch the differences with "wrapper" components

Is there a way to generate TypeScript interfaces from Flow types so you don't need to maintain both?
I don't know.

@nandorojo
Copy link
Contributor

nandorojo commented Nov 10, 2020

In my app, I use patch-package to change @types/react-native's read-only types from type to interface. This change addresses the caveat I mentioned above.

I then use declaration merging in my own files.

Until there is a better solution, I'll be sticking with that. I can put together a gist if it would be useful.

Re: the questions:

  1. TypeScript declaration merging
  2. Yes, it should do TypeScript declaration merging for you. I don't know about Flow.

I'm not a big fan of creating my own View just for type support, so I prefer that approach.

@baptisteArno
Copy link

In my app, I use patch-package to change @types/react-native's read-only types from type to interface. This change addresses the caveat I mentioned above.

I then use declaration merging in my own files.

Until there is a better solution, I'll be sticking with that. I can put together a gist if it would be useful.

Re: the questions:

  1. TypeScript declaration merging
  2. Yes, it should do TypeScript declaration merging for you. I don't know about Flow.

I'm not a big fan of creating my own View just for type support, so I prefer that approach.

What about when you need to overwrite an existing field? For example if I need to add "fixed" in position type:

declare module "react-native" {
  interface FlexStyle {
    position?: "absolute" | "relative" | "fixed";
  }
}

It complains about position being already declared.

@nandorojo
Copy link
Contributor

I've tried to avoid fixed position and things that RN doesn't support, but when you can't, you might need to use patch-package to edit the react native types package.

@janlat
Copy link

janlat commented Jan 14, 2021

@baptisteArno Should work if you change FlexStyle to ViewStyle

@nandorojo
Copy link
Contributor

Some good news: @types/react-native seems to have implemented the fix suggested here, which means declaration merges works for TypeScript in the case of Pressable.

You can now do this in your app:

// react-native-web/overrides.ts

declare module 'react-native' {
  interface PressableStateCallbackType {
    hovered?: boolean
    focused?: boolean
  }
}

And you'll get type support for Pressable, for instance.

I think it would make sense to have one TS file in react-native-web that includes all of the declaration merging, such as the code sample above. This way, anyone who uses react-native-web gets upgraded types. For those who do not, nothing changes.

@necolas necolas removed the question label Feb 1, 2021
@nandorojo
Copy link
Contributor

Sorry to keep adding messages here, but I think the solution is to add a declaration-merging.ts file /types. Similar to the flow files that are already there, it will look like this:

declare module 'react-native' {
  interface PressableStateCallbackType {
    hovered?: boolean
    focused?: boolean
  }
  interface ViewStyle {
    transitionProperty?: string
    transitionDuration?: string
  } 
  // ...etc
}

I only added a few types there, but it would be easy to add the rest of the RNW-specific props. This would solve the problem for TypeScript users.

@padapada09
Copy link

You should install react-native types

   npm install @types/react-native

And redirect them to react-native-web types in tsconfig.json

    "paths": {
      "@types/react-native-web": ["../node_modules/@types/react-native"],
      "react-native": ["../node_modules/react-native-modules"]
    }

@comp615
Copy link
Contributor

comp615 commented Mar 13, 2021

I played around most of today with Flow and trying to get the types to just pop up using flow-copy-source. I was able to get flow updated to the latest version, including re-copying some of the vendored code from RN, and doing some code mods. See that all here: https://github.com/comp615/react-native-web/tree/flow.

However, when I tried to use this in a sample expo projects. I still got a fair number of flow errors from:

  • modules (i.e. normalize-css-color) because presumably when flow goes to look at the .js.flow file, those imports are still there and it still tries to resolve them.
  • src / test files. These shouldn't really be scanned by the test project. I played around with various flowconfigs to ignore them, but that seems like something the project should have to do. Potentially caused by using yarn link instead of a normal build.

The code to update flow from that branch is 99% good, with the package change to copy sources being the bit I'd pull out.

I'd like to try it again with a create-react-app or Twitter to see if a different project setup works more easily. Expo seems to just work with react-native because RN is directly linked from source and not compiled through babel.

As a side musing...flow internally via types-first seems to keep some externally visible signature for each file. It would be awesome if it would just dump those as .flow.js files so we didn't have to copy source as lib authors.

@necolas necolas removed this from the 0.16 milestone Mar 29, 2021
@necolas necolas added this to the 0.16 milestone Apr 9, 2021
necolas pushed a commit that referenced this issue Apr 9, 2021
@hosseinmd
Copy link
Contributor

@necolas is it solved typescript issue?

@nandorojo
Copy link
Contributor

The current commit doesn't fix it for TypeScript users.

I mentioned the TS solution in comment above.

react-native-tvos uses the strategy I mentioned. You can see their types declaration file here.

Maybe there's a way to turn the generated flow files into TS types.

@kopax-polyconseil
Copy link

I am experience errors with accessibilityRole typing on the Web : #2189 (comment)

Is there a support for TypeScript here we can use or a workaround ?

@orlando
Copy link

orlando commented Jan 27, 2022

I am experience errors with accessibilityRole typing on the Web : #2189 (comment)

Is there a support for TypeScript here we can use or a workaround ?

I answered this here #2194 (comment).

Tl;dr add this to your types file.

declare module 'react-native' {
  interface TextProps {
    // https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/modules/AccessibilityUtil/propsToAccessibilityComponent.js#L12
    accessibilityLevel?: number;
    accessibilityRole?:
      | 'heading'
      | 'label'
      | 'list'
      | 'listitem'
      | 'main'
      | 'region'
      | 'strong';
  }
}

@yawnr
Copy link

yawnr commented Mar 30, 2022

@nandorojo pardon my ignorance, but doesn't declaring the module like that wipe out all of the existing type definitions from @types/react-native? If I do something like

// types/rn-web.d.ts
import * as RNTypes from 'react-native';

declare module 'react-native' {
  export type ImageRequireSource = string;
}

then all of my existing RN imports error with a no exported member error. Is there some config you're using to be able to extend the module declaration?

@nandorojo
Copy link
Contributor

Add this:

import 'react-native'

See: https://github.com/nandorojo/solito/blob/master/example-monorepos/blank/packages/app/rnw-overrides.tsx

I used .tsx there, I believe that worked for me.

rnike pushed a commit to VeryBuy/react-native-web that referenced this issue Sep 13, 2022
@juanpprieto
Copy link

This did it for me

npm i --save-dev @types/react-native-web@npm:@types/react-native

@erickreutz
Copy link

What is the current state of this with typescript?

@blazejkustra
Copy link

blazejkustra commented Aug 7, 2024

There is no DefinitelyTyped package for react-native-web. I'm not entirely sure how one would work (efficiently).

Until now. Happy to announce that types for react-native-web are now available on DefinitelyTyped! 🎉

The package comes with a react-native-web global declaration, so you can use it in your project type-safe.

import { AppRegistry } from 'react-native-web';

And it also extends the react-native types, so you can use react-native components in your react-native-web project.

import { View } from 'react-native';

<View style={{ position: 'fixed' }} />

Installation

npm install --save-dev @types/react-native-web

To extend the react-native types, you have to supply react-native-web as a member of the types compiler option in tsconfig.json.

{
  "compilerOptions": {
    "types": ["react-native-web"]
  }
}

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

No branches or pull requests