Crab Update — TypeScript and Routes

TypeScript is available

This was the most requested feature on Crab’s release. The React CLI tool Crab now supports TypeScript in all generated code. Going forward, TypeScript is intended to be a first-class citizen of Crabland, meaning that any future code generators or functionality added should fully support TypeScript.

If you want access to these TypeScript component generators just install Crab: npm install -g crab-cli.

There is a key issue here, though. With Redux installed there was an arguable case that you might not always want a Redux component and it shouldn’t default, but always require the explicit-r flag in generators.

The same can’t be said of TypeScript. It doesn’t seem reasonable at all for a project started as a TS project to have its components and resources generating themselves as plain old povo ES6 modules.

Adding a .crab file

There are two solutions to this. One is to have Crab check what kind of project it seems to be, looking around for .ts and .tsx files and then generating accordingly. That is fraught with peril though. What if you make your components in a new directory? Should it look through parents? How far? The other solution (and the one I’ve opted to go with) is to explicitly declare the project as a TypeScript project. To that end I’ve created a .crab file, which contains a tiny amount of “config”. At most it will be the following at present.

"redux": true,
"typescript": true

This file isn’t mandatory, and it’s only created if TypeScript and/or Redux are created. The file is intended to be harmless and its existence is not a requirement. If you delete it, crab’s naive behaviour is restored and any TypeScript or Redux requirements need to be explicitly flagged.

I resisted this necessity for some time, but ultimately there isn’t a better solution. There is no intent to make it complex config, though. If I wanted a clusterfuck of config options that make no sense I’d just use Webpack.

As an aside to this, if there is a Redux or TypeScript option set in the crabfile there’s no way to make it not add those features. Adding support for --no-typescript for example, will force the typescript default to be true for any command where it’s not explicitly set false. This is a feature of CommanderJS.

Component Re-exporting

With this release of Crab there are a few opinions creeping in. One is that I’ve never been a big fan of the enormous collection of import at the top of a lot of React components. Perhaps my Ember sensibilities, as Ember’s internal resolver removes the need. Nevertheless, some of it is inevitable, such as pulling in bits of React, Redux, etc. But the long list of child components potentially used in a complex component seems excessive.

To combat this I’ve used a re-exporting pattern, with a components.js file importing the module default and then re-exporting the named component. There are a number of benefits to this.

Creating a component with crab will append that component’s re-export to this file, and with TypeScript will append a re-export and a new type definition.

Crab’s own components/App.js includes the following line.

import { Home, AboutCrab, WhatNext } from './components';

which replaces three lines of code:

import Home from './Home';
import AboutCrab from './AboutCrab';
import WhatNext from './WhatNext';

Though the saving of two lines is trivial, this could well extend out to five, ten, or fifteen components. Note that it’s not always necessary either. For a single import or two you can just do it normally. Nothing forces it.

Crab will check for the existence of components before trying to add new ones. So if you already have components/List and you add components/users/List it will instead export this component as UsersList to differentiate. Note that this is per-component, so adding components/users/Item when there’s no components/Item won’t trigger this behaviour, leaving you with UsersList and Item. It’s up to you if you want to manually export the second as UsersItem, and doing so will not be harmful.

In fact, if you hate this functionality and want to remove it entirely, just delete the components/components.js file. Aside from possibly needing to refactor anywhere it was being used, all other code generation will continue to work happily without it.

Route Generation

You can now generate a “route” directly from the Crab interface. In React, there’s nothing particularly special about a route’s component, and that’s all it generates. The big difference is that the component generated is imported into the components/App.js component and inserted into the router.

Route generate adds several parameters, all of which are optional. The first is --path [path]which is the URL that matches this route. The other is--menu. Exact just adds the “exact” match condition on the path to prevent paths like / matching all the time. The --menu option will add an entry to the current nav.

crab generate route OurServices --path our-services --menu --functional

If it seems like a lot of typing, note that this could just have been written like so, as the above is exactly what the defaults will do automatically.

crab g route OurServices -f --menu

Note that the generator for a route is still just fundamentally generating a component, and the component generator options still apply, including TypeScript and Redux support.

This is relatively complex functionality and has some requirements to work. Most particularly it requires that you have a components/App.js and that the app component contains a Switch block. It inserts the route as the last entry in that block. Though switch is not mandatory for React Router itself it’s considered best-practise and crab requires it so it can be certain. The same thing applies to menu addition. Crab looks for a list of Navlink or Link components (three or more), and appends a new one with the same general vibe.

If any of the requirements can’t be met safely, and Crab isn’t able to insert the route or add the menu item, it will just warn you and handle the bits it can. At very least you’ll get the component, and it will be up to you to insert it.

This functionality is about as good as you can get in React given that there are no conventions to work with. Ultimately, these features will work less effectively as you customise and modify a created application. They have a diminishing return as development goes on. (By that I refer specifically to features like auto-route insertion. The value of simple generation of component boilerplate remains strong.) This is ok, though. The goal is to provide tools to help scaffold out the early stages of an app.

Other Minor Changes

There have also been some minor bug fixes and improvements, specifically

  • Improving of the CLI output so that things were better grouped and a lot less.. orange.
  • Fixed use of class=" in one of the templates
  • Modified the redux structure to explicitly export constants in a separate file.

As a minor and final feature I also added another small command option: crab add. At present this is limited to a single option, tailwind. This was done for my own convenience as I was doing a lot of Tailwind-based prototyping. Using crab add tailwind will install Tailwind, adding PostCSS config, and add tailwind’s required PostCSS directives to a newly created styles/app.css that needs to be imported into the index.js file.

A final comment is to say that I do not use TypeScript in my own work. If there is anything wrong with the TypeScript implementation here, especially when it comes to the combination of TypeScript and Redux, please let me know.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Matt Burgess

Matt Burgess

Senior Web Developer based in Bangkok, Thailand. Javascript, Web and Blockchain Developer.