How to get code completion for typed react component properties?
Asked Answered
C

3

15

I am using react and mobx-state-tree and I use @inject to inject the stores into my component. So in the end I access the store via this.props.uiStore inside of my component.

Unfortunately Visual Studio Code can't infer the type of my store, so I don't have any code completion for the properties. I wondered if I could use jsDoc for that (since it works for methods quite well), but couldn't find a way. I was thinking of something along the lines of:

export default class DeviceMirror extends React.Component {
  /**
   * @namespace
   * @property {object}  props
   * @property {UiStore}  props.uiStore
   */
  props

But it does not work.

Churl answered 1/3, 2019 at 9:5 Comment(3)
It doesn't work that well with Webstorm either. I feel your pain.Bimestrial
Does the autocomplete for MST stores work for you otherwise?Inverter
@Inverter yes it doesChurl
S
9

You can use JSDoc to make Visual Studio Code properly infer React components props, the trick is the use of @extends {Component<{type def for props}, {type def for state}>}}:

file: store.js (this is just an example file to demonstrate how the intellinsense will catch definitions but any object, class, typedefiniton, and probably even json would do. If you can import it and reflect it, you can link it to a Component props)

    class CustomClass {
        // ...
    }
    // Note: exporting an object would also do
    export default class UiStore {
        /**
         * @type {string} this is a string
         */
        str = null
        /**
         * @type {number} this is a number
         */
        num = null
        /**
         * @type {Date} this is a Date
         */
        dat = Date
        /**
         * @type {CustomClass} this is a CustomClass
         */
        cls = null
    }

file: test.jsx

    import React, { Component } from 'react';
    import PropTypes from 'prop-types';
    import UiStore from './store';

    /**
     * @typedef Props
     * @prop {UiStore} uiStore
     */

    /**
     * @extends {Component<Props, {}>}}
     */
    export default class DeviceMirror extends Component {
        static propTypes = {
            // not needed for intellisense but prop validation does not hurt
            uiStore: PropTypes.instanceOf(UiStore),
        }
        /**
         * @param {Props} props - needed only when you don't write this.props....
         */
        constructor(props) {
            super(props);
            this.s = props.uiStore.str;
        }
        render() {
            const { uiStore } = this.props;
            return <p>{uiStore.str}</p>;
        }
    }

VSCode can use this kind of declarations and will offer intellisense and code completion. both from inside and outside the component file:

screenshot of vscode

Silurid answered 7/3, 2019 at 15:3 Comment(3)
Thats great to know! However, I need to redefine all properties (which I have defined already in MST format), right?Churl
No you don't have to redefine anything as long as code completion on the desired type is already possible outside the React component. This is just a matter of correctly assigning the generic types of the parameterized class Component<Props, State> (or PureComponent) with @extends {Component<{}, {}>}. You can even use an object instance as the type definition.Silurid
Note that you can skip State definition with an empty object {} as a placeholder.Silurid
A
3

There's no way to go from a TypeScript type declaration to a mobx-state-tree model definition. However, if you write the mobx-state-tree model definition, then you can generate the TypeScript type from it; Using a MST type. So you would have to convert your existing interfaces, but at least you wouldn't have to keep maintaining two copies of the same information.

import { types, Instance } from 'mobx-state-tree';

const uiStore = types.model({
  prop: types.string,
});
export type IuiStore = Instance<typeof uiStore>;

export default uiStore;
Azral answered 7/3, 2019 at 14:8 Comment(2)
Can you elaborate on this? How would I use IuiStore in my component to get code completion?Churl
IuiStore could be a type oк interface and the way to use is just to assign your props to a new variable inside the component like let myProps: uiType = this.props.uiStore. I personally prefer to use decorators in my index file and keep a component as a pure function so it's easy to test it and it's not tie-up with any state management libs.Azral
C
0

For those of you who also work with MST stores, here is how you get code completion in visual studio code:

import DbStore from '../stores/DbStore'
import UiStore from '../stores/UiStore'    

/**
 * @typedef Props
 * @prop { typeof UiStore.Type } uiStore
 * @prop { typeof DbStore.Type } dbStore
 * @prop { boolean } editMode
 * @prop { Array } history
 * ....
 */
/**
 * @extends {React.Component<Props, {}>}}
 */
@inject('dbStore')
@inject('uiStore')
@observer
class TestView extends React.Component { ....
Churl answered 18/5, 2020 at 10:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.