I finally finished the migration of the AdminPanel from JS/JSX to TS/TSX and fluxxor to Redux. I want to show you a short summary of pitfalls which I encountered and how I solved them.

Not using npm

When working with JavaScript you almost can't get around using npm. However, I'm not a fan of including too many dependencies in the code and the npm makes it too easy to introduce them.
My workflow of adding a new library is therefore a bit different.
One thing to note is, that the TypeScript compiler uses the module system of SystemJS by default. The syntax is basically a new standard which will be implemented by future browsers so that the SystemJS lib will not be required anymore.

Disclaimer: This post only includes a small part of the libraries which I had to include. For the full code, better look at the GitHub repository.

TypeScript Configuration

To be able to use aliases in imports, like import Redux from "redux"; instead of defining the path of the library in every ts/tsx file, I have to add them manually at the tsconfig.json in the paths:

  "compilerOptions": {
        "paths": {
          "*": [ "*" ],
          "react": [ "Scripts/react/index" ],
          "redux": [ "Scripts/redux/index" ],
          ...
        }
   }

Running the code

To use the code at runtime, so that the browser is able to resolve and run, I had to add SystemJS and the generated code as scripts in the index.(ss)html:

<script src="@Path['~/content/js/system-production.js']"></script>
<script src="@Path['~/content/js/app.js']"></script>

The app.js was generated by the TypeScript compiler and is actually registering the modules/components at SystemJS, example:

// generated code at app.js:
System.register("components/App", ["react", "react-dom", "react-redux", "components/Layout", "stores/store"], function (exports_37, context_37) {…}

This raises the question how SystemJS should know what "redux" or "react" means? We have to "map" them manually:

<script>
            System.config({
              map: {
                'react': '@Path['~/content/js/react.js']',
                'redux': '@Path['~/content/js/redux.js']'
              }
            });
</script>
  • To run the app, I'm calling System.import on the App-Component:
System.import("components/App")
                .then(m => console.log('App module resolved'))
                .catch(err => console.error(err));

This component directly calls ReactDOM.render:

// components/App.tsx:
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { Layout } from "./Layout";

import configureStore from "../stores/store";

const store = configureStore(…);

// This here is our entry point
ReactDOM.render(
    <Provider store={store}>
        <Layout />
    </Provider>, document.getElementById("content")
);

Voilà, the app renders :)

Conclusion

All in all, the setup was a bit tedious, because I had to read through all possible documentations. I found no "boilerplate" example of setting up an environment like mine (React & Redux & TypeScript in a VS project without npm). However, I think it was worth it. I'm now very productive at extending the AdminPanel. Using types makes refactoring easier and obvious bugs are found faster by the compiler without running the code in the browser.
When selecting new libraries I now have to look after TypeScript definition files, which is a bit tedious... another point which prevents me to add new dependencies without explicitly thinking about the consequences.

What's next

It seems like bootstrap is already 'old-school' and material-ui is the new 'standard' for React based web-apps. It looks great, but I'll have to see if it makes sense for the project.

Maybe I'll add some unit tests for the TypeScript code, too. As I read in some tutorials this seems to be easy :)