preact-cli icon indicating copy to clipboard operation
preact-cli copied to clipboard

Using Mobx-React works with Preact Watch, but not Preact Build

Open rschristian opened this issue 4 years ago • 4 comments

Do you want to request a feature or report a bug?

Bug (I believe)

What is the current behaviour?

I'm using mobx and mobx-react-lite to add global state to my TypeScript Preact project, and I'm using the functional component syntax, so a component with mobx looks something like const component: preact.FunctionalComponent = observer(() => { ... });.

This works as expected while running the development server (preact watch), fully functioning, no problems. However, if I try to build the project with preact build, I'm given the following error:

✖ ERROR ./node_modules/mobx-react-lite/dist/observer.d.ts
ERROR in ./node_modules/mobx-react-lite/dist/observer.d.ts(1,23):
TS2688: Cannot find type definition file for 'react'.

Odd that only building needs the types, but okay. So I install @types/react, but I'm then met with:

TS2345: Argument of type '() => preact.VNode<{}>' is not assignable to parameter of type 'FunctionComponent<{}>'.   Call signature return types 'VNode<{}>' and 'ReactElement<any, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)> | null' are incompatible.     The types of 'type' are incompatible between these types.       Type 'string | ComponentClass<{}, {}> | FunctionComponent<{}>' is not assignable to type 'string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)'.         Type 'ComponentClass<{}, {}>' is not assignable to type 'string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)'.           Type 'ComponentClass<{}, {}>' is not assignable to type 'new (props: any) => Component<any, any, any>'.             Property 'refs' is missing in type 'Component<{}, {}>' but required in type 'Component<any, any, any>'. 

As I understand it, the preact.VNode is incompatible with whatever type is defined in @types/react, which does make sense, as React has it's own Node type.

But how exactly do I get around this? I assume because the dev server works, this is an error with the aliasing, but only with the build script.

If the current behaviour is a bug, please provide the steps to reproduce.

Wrap a component with mobx-react-lite's observer while using TS:

const component: preact.FunctionalComponent = observer(() => { 
... 
});`

What is the expected behaviour?

I'd expect to be able to build the application without experiencing that type issue. Again, not an expert here, but I believe this is due to an alias not being properly resolved, so I'd like to make sure this works.

Please mention other relevant information.

My config file:

export default {
    webpack(config, env, helpers, options) {
        const css = helpers.getLoadersByName(config, 'css-loader')[0];
        css.loader.options.modules = false;

        // Sets default import to 'src/'
        config.resolve.modules.push(env.src);

        if (!env.isProd) {
            config.devServer.proxy = [
                {
                    path: '/api/v1',
                    target: 'http://localhost:8000',
                },
            ];
        }
    },
};

Result from running preact info:

Environment Info:

  System:
    OS: Linux 5.0 Ubuntu 19.04 (Disco Dingo)
    CPU: (4) x64 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
  Binaries:
    Node: 12.14.1 - /usr/bin/node
    npm: 6.13.6 - /usr/bin/npm
  Browsers:
    Firefox: 72.0.1
  npmPackages:
    preact: ^10.2.1 => 10.2.1 
    preact-cli: ^3.0.0-next.19 => 3.0.0-rc.7 
    preact-router: ^3.1.0 => 3.1.0

rschristian avatar Jan 25 '20 17:01 rschristian

Yes building is what does a more thorough type checking.

This seems like a bug in Preact core typings and nothing to do with CLI specifically // @marvinhagemeister

prateekbh avatar Jan 25 '20 18:01 prateekbh

Should I then move this over to the Preact repository instead?

rschristian avatar Jan 26 '20 20:01 rschristian

This is a known issue with TS. It doesn't allow anyone to alias typing packages and thus a library written for react will require you to install @types/react to silence the first class of errors.

But then you're still faced with the issue of mixing both React with Preact types. The only solution to that I've seen so far is re-casting the type.

Something like the following pseudo code (currently on mobile). You should get the idea:

import { observer as mobxObserver } from "mobx-react-lite";

// Re-typing functions with generics by assigning them to variables
// doesn't seem to work, so we create a new function :/
function observer<P>(props: P) {
  return mobxObserver(props as any);
}

If somebody knows a better solution to this I'd love to hear it. This would help us a lot, because it's a frequent question we get over at the Preact issue tracker.

marvinhagemeister avatar Jan 26 '20 22:01 marvinhagemeister

The other solution is writing your own @types/react

See https://github.com/preactjs/preact/issues/2150#issuecomment-558622551

ForsakenHarmony avatar Feb 06 '20 09:02 ForsakenHarmony