16

When defining react components using typescript we can write something like:

class SomeComponent extends React.Component<PropInterface, StateInterface> {
  // ...
}

Is there a way do the equivalent using jsdoc annotations and have props type-checked.

lorefnon
  • 12,875
  • 6
  • 61
  • 93

4 Answers4

34

I prefer following form (es2015 + @types/react):

/**
 * @typedef {object} Props
 * @prop {string} className
 * @prop {number} numberProp
 *
 * @extends {Component<Props>}
 */
export default class SomeComponent extends Component {
    render() {
        return (
            <div className={this.props.className}>
                {this.props.numberProp}
            </div>
        );
    }

}
artin
  • 1,764
  • 1
  • 17
  • 23
5

In case someone is searching for an alternate solution. Regarding to this Typescript issue you can also achieve it like this.

import React, { Component } from 'react';
import PropTypes from 'prop-types';

/**
 * @augments {Component<{onSubmit:function, text:string}>}
 * @param {object} event - Input event
 * @return {React.ReactElement} - React component
*/
class Test extends Component {
  handleInput = (event) => {
    event.preventDefault();
    this.props.onSubmit(event.target.value);
  };

  render() {
    const { text } = this.props;
    return <div>Hello, property :O {text}</div>;
  }
}

Test.propTypes = {
  onSubmit: PropTypes.func.isRequired,
  text: PropTypes.string.isRequired,
};

export default Test;
datoml
  • 5,554
  • 4
  • 21
  • 28
  • Run-time `PropTypes` checks mixed up with static `typescript` checks. I would drop `PropTypes` since it does not make sense to use both. – artin Nov 30 '17 at 16:33
4

This works, though it may not be so good.

// Foo.jsx
import * as React from 'react';

/**
 * @type {{ new(props: any): {
     props: { a: string, b: number },
     state: any,
     context: any,
     refs: any,
     render: any,
     setState: any,
     forceUpdate: any
   } }}
 */
const Foo = class Foo extends React.Component {
  render() {
    return <div className={this.props.a}>{this.props.b}</div>;
  }
};
export default Foo;

// import Foo and use it in .tsx or .jsx file
import Foo from './Foo';

<Foo/>; // error: Type '{}' is not assignable to type '{ a: string; b: number; }'
<Foo a='a' b={0}/>; // OK
kimamula
  • 11,427
  • 8
  • 35
  • 29
  • Thanks for the answer. This works. Can you also suggest how to flag passing unspecified props as errors? eg. in your example if I call something like: ``, I don't get any errors. – lorefnon May 04 '17 at 04:43
  • Hmm, that's strange. I get an error `Property 'c' does not exist on type 'IntrinsicAttributes ...` when I call `` with `"compilerOptions": { "target": "es5", "allowJs": true, "checkJs": true, "jsx": "react" }`. I am using TypeScript 2.3.2. – kimamula May 04 '17 at 05:04
  • I am trying with all files as js files (including the one that imports it), along with `// @ts-check`. tsconfig: ` { "compilerOptions": { "noEmit": true, "allowJs": true, "jsx": "React", "checkJs": true, "allowJs": true }, "include": [ "./src/" ] } ` – lorefnon May 04 '17 at 05:20
  • Actually when both files are js the type validations are not happening at all. – lorefnon May 04 '17 at 05:57
  • I created [a repo on github](https://github.com/kimamula/ts-jsdoc-support-react), in which type checking is working as expected in (probably) the same condition as yours. – kimamula May 04 '17 at 06:18
  • OK. I was not assigning to const Foo and exporting that. This is weird, but works. Thanks a lot for all the help. – lorefnon May 04 '17 at 06:34
3

If you use PropTypes,

something like this works for me:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

/**
 * Test component
 * @augments {Component<Props, State>}
 */
class Test extends React.Component {
  // ...
}

Test.propTypes = {
  title: PropTypes.string.isRequired,
}

export default class Test1 extends React.Component {
  render() {
    return <Test  />
  }
}

vscode proptypes intellisense

gaurav5430
  • 12,934
  • 6
  • 54
  • 111