Definitelytyped: Using react-router in react ES6 component with Typescript

Created on 17 Aug 2015  路  9Comments  路  Source: DefinitelyTyped/DefinitelyTyped

Basically, I can not find a way to make Typescript understand react-router's new way of using the context in react ES6 components. Mainly, using code like this to get the react-router to function in react ES6 classes:

import React from 'react';
class LoginPage extends React.Component {
  ...
  onChange() {
    this.context.router.replaceWith('/');
  }
  ...
}

LoginPage.contextTypes = {
  router: React.PropTypes.func.isRequired
};

export default LoginPage;

Gives an error like this: Property 'contextTypes' does not exist on type 'typeof LoginPage'.

Every React component using ES6 and react-router will need to do this since react's new ES6 features does not support mixins. I am linking the discussion of this issue from the react-router repo (https://github.com/rackt/react-router/issues/975). Has anyone seen this before? How are people using Typescript, React ES6 components, and react-router?

Is this an issue with the react-router type definitions? react type definitions? none of the above? I'm not sure.

Most helpful comment

Ok, just figured out that TS wanted contextTypes typed, here is the final version for anyone else that may run into this. Not an issue with the react, or react rout-er type definitions:

import React from 'react';
class LoginPage extends React.Component {

  static contextTypes: React.ValidationMap<any> = {
  router: React.PropTypes.func.isRequired
};

...
  onChange() {
    this.context.router.replaceWith('/');
  }
  ...
}

Hopes that helps any other poor souls trying to use react-router with react ES6 classes in typescript.

All 9 comments

After 3hrs, solved it with ES7 syntax:

import React from 'react';
class LoginPage extends React.Component {

  static contextTypes = {
  router: React.PropTypes.func.isRequired
};

...
  onChange() {
    this.context.router.replaceWith('/');
  }
  ...
}

export default LoginPage;

Ok, still not solved, as now Typescript is complaining about this syntax (the new way react-router is used in ES6 react classes). I'm getting the following TS error now, but this is the only way to use react-router in react ES6 classes:

(51,19): Type 'typeof Unauthorized' is not assignable to type 'ComponentClass<any>'.
  Types of property 'contextTypes' are incompatible.
    Type '{ router: Validator<any>; }' is not assignable to type 'ValidationMap<any>'.
      Index signature is missing in type '{ router: Validator<any>; }'.

Narrowed down the culprit to the React TS def. file at:

interface ComponentClass<P> {
        new(props?: P, context?: any): Component<P, any>;
        propTypes?: ValidationMap<P>;
        contextTypes?: ValidationMap<any>;
        childContextTypes?: ValidationMap<any>;
        defaultProps?: P;
    }

Commenting out contextTypes?: ValidationMap<any>; makes it work, but I am nervous of the repercussions. Has anyone else seen this, or is everyone still using React without ES6? Heading over to the TS React definition section, as this is obviously an issue for them, not the React-Router def. Although interestingly enough, the React def. file has implications on whether you can use react-router or not.

Ok, just figured out that TS wanted contextTypes typed, here is the final version for anyone else that may run into this. Not an issue with the react, or react rout-er type definitions:

import React from 'react';
class LoginPage extends React.Component {

  static contextTypes: React.ValidationMap<any> = {
  router: React.PropTypes.func.isRequired
};

...
  onChange() {
    this.context.router.replaceWith('/');
  }
  ...
}

Hopes that helps any other poor souls trying to use react-router with react ES6 classes in typescript.

Thanks to you and to Marius Rumpf for making me realize that said contextTypes must be a static property, awesome, don't know what I would do without all of you people who document your findings!

Great! thanks Mauricio, is this documented somewhere? If not, it should!!

Thanks, this helped me as well!

This was very helpful for me too thanks! :)

A couple of other notes in case it's helpful to others stumbling here to do programmatic react-router redirection as I was. afaict react-router no longer injects 'router'
https://github.com/rackt/react-router/search?utf8=%E2%9C%93&q=childContextTypes&type=Code

I instead used history, and also for whatever reason needed to cast the context as 'any' since context.history was flagged as not existing

static contextTypes: React.ValidationMap<any> = {
  history: React.PropTypes.object.isRequired
};
...
(this.context as any).history.pushState(null, '/');

@danielbanfield it was flagged as not existing because the type of context is context: {}; which means history is not available on an empty object.

Ideally it would be nice to change the definition of Component to something like this:

class Component<P, S, C> implements ComponentLifecycle<P, S, C> {
    ...
    context: C
}

It's easy then to pass a custom context type.

@ppoliani love your idea!
This will be very easy when typescript supports generic type variables https://github.com/Microsoft/TypeScript/issues/2175

Was this page helpful?
0 / 5 - 0 ratings