Closed necolas closed 3 years ago
Here is a useful dialog about TypeScript in RNW: https://github.com/necolas/react-native-web/issues/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.
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.
@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
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.
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.
What's the best way to export the types? Babel is currently stripping them when it compiles the files.
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
"@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.
two cases:
components.tsx
and components.web.tsx
, and always use components.tsx
as the source when importing.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.
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.
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.
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.
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"]
},
...
}
}
@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?
Could TypeScript declaration merging solve this?
That's what I'm using in my current project.
// 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.)
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
.
I think ReactNative could have web type too. ReactNative supported specific type for android or iOS that could support specific type for web too.
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
I do not agree with you because React Native added web to official documentation but not added react-native-macos or react-native-windows
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.
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:
I'm not a big fan of creating my own View
just for type support, so I prefer that approach.
In my app, I use
patch-package
to change@types/react-native
's read-only types fromtype
tointerface
. 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:
- TypeScript declaration merging
- 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.
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.
Similarly, borderRadius
, translateY
, translateX
, and fontSize
only allows number
in RN, but in RNW it can also be set to a string. RNW also allows me to use the AccessibilityRole
's list
and listitem
, not present in RN's type.
I actually do this naive transformation in a node script on postinstall:
const RN_TSD = process.cwd() + '/node_modules/@types/react-native/index.d.ts';
const raw = fs.readFileSync(RN_TSD);
let transformed = raw.toString();
transformed = transformed.replace('borderRadius?: number;', 'borderRadius?: number | string;');
/ * ... */
fs.writeFileSync(RN_TSD, transformed);
I know it will break on a change in the type, but it satisfies me for now.
@baptisteArno Should work if you change FlexStyle
to ViewStyle
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.
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.
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"]
}
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:
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.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 is it solved typescript issue?
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.
I am experience errors with accessibilityRole
typing on the Web : https://github.com/necolas/react-native-web/issues/2189#issuecomment-1008886405
Is there a support for TypeScript here we can use or a workaround ?
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 https://github.com/necolas/react-native-web/issues/2194#issuecomment-1022714079.
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';
}
}
@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?
Add this:
import 'react-native'
I used .tsx
there, I believe that worked for me.
This did it for me
npm i --save-dev @types/react-native-web@npm:@types/react-native
What is the current state of this with typescript?
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! :tada:
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' }} />
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"]
}
}
I'm looking for input from people who use typed JavaScript with this library.
react-native-web
exports?View
?